1. "DB가 왜 이렇게 느리지?"
RAG(Retrieval-Augmented Generation) 시스템을 공부하다가 이런 사례를 접했습니다.
대량의 문서를 다루는 사례에서, 임베딩 벡터를 PostgreSQL에 저장하고 pgvector 플러그인으로 검색하면 어떻게 될까?
가능은 합니다. 그런데 문서 수가 늘어날수록 검색 속도 문제가 생깁니다.
사용자가 질문을 하면 답변이 나오기까지 수 초가 걸리는 상황. "DB에서 검색하는 데만 2.5초?" 데이터가 많아질수록 이 숫자는 선형으로 늘어납니다. 서비스 불가능 판정이죠.
문서에서 본 설명이 이랬습니다. "RDB(관계형 DB)는 정확한 값을 찾는 데 특화된 놈이야. 벡터 검색은 벡터 DB(Vector Database)를 써야지."
Pinecone 같은 전문 벡터 DB로 마이그레이션하면 어떻게 될까요? 기존 방식 대비 검색 속도가 크게 개선된다고 한다. 같은 데이터인데 도대체 왜 이렇게 차이가 나는 걸까요?
2. 처음엔 뭐가 이해가 안 갔나
혼란 1 - "일반 DB랑 뭐가 다른가?"
"데이터 저장하고 SELECT 하는 건 똑같잖아. 왜 굳이 새 DB를 배워야 해?" Elasticsearch 같은 검색 엔진도 있는데, 왜 또 새로운 게 필요한지 납득이 안 됐습니다.
혼란 2 - "어떻게 그렇게 빠른가?"
고등학교 수학 시간에 배운 벡터 내적(Dot Product)을 생각해보면, 10만 개 벡터를 다 계산하려면 엄청난 연산량이 필요할 텐데, 어떻게 0.05초 만에 가능한지 미스터리였습니다.
3. 어떤 포인트에서 이해가 됐나
이해하는 데 결정적이었던 비유는 "도서관"이었습니다.
일반 DB (RDB) = 도서 번호(ID)로 찾기
도서 검색대에서 "청구기호 800.12-34"를 입력하면, 사서가 정확히 그 위치로 가서 책을 꺼내옵니다.
- 쿼리:
SELECT * FROM books WHERE id = 123 - 특징: 정확한 일치(Exact Match)만 가능. 123번 책이 없으면 못 찾음.
- 속도: 인덱스(B-Tree) 덕분에 엄청 빠름.
벡터 DB = "내용이 비슷한" 책 찾기
사서에게 "주인공이 우주 여행을 하는데, 약간 슬픈 내용의 소설 있어?"라고 물어보는 것과 같습니다. 사서는 도서 번호가 아니라, 책의 내용(의미)을 파악하고 있어야 합니다.
- 쿼리:
Find books similar to [0.1, 0.8, 0.3, ...] - 특징: 의미적 유사성(Semantic Similarity) 검색. 정확히 일치하는 단어가 없어도 됨.
- 속도: 일반적인 방법(모든 책 다 뒤지기)으로는 엄청 느림. 그래서 특별한 기술(HNSW 등)이 필요함.
이 비유로 이해했습니다. 벡터 DB는 "정확한 값이 아니라, 가장 비슷한 값을 찾는 데(Approximate Nearest Neighbor)" 특화된 DB라는 것을요.
4. 벡터 DB vs 일반 DB - 결정적 차이
| 특징 | 일반 DB (MySQL, PostgreSQL) | 벡터 DB (Pinecone, Chroma) |
|---|---|---|
| 검색 방식 | 정확한 키워드/ID 일치 | 의미적 유사도 (Cosine Similarity) |
| 데이터 타입 | 정수, 문자열, 날짜 | 1536차원 벡터 (실수 배열) |
| 인덱스 | B-Tree (정렬 기반) | HNSW, IVF (그래프/군집 기반) |
| 쿼리 예시 | WHERE content LIKE '%AI%' | vector_search(embedding, top_k=5) |
| 주 용도 | 결제, 회원관리, 게시판 | 챗봇, 추천 시스템, 이미지 검색 |
RDB는 "틀리면 안 되는 데이터"(돈, 재고)를 다루고, 벡터 DB는 "비슷한 게 좋은 데이터"(추천, 검색)를 다룹니다.
5. 기술 - 인덱싱 (HNSW)
벡터 DB가 기존 방식보다 훨씬 빠른 비밀은 HNSW (Hierarchical Navigable Small World)라는 인덱싱 알고리즘에 있습니다. 이름이 엄청 길고 어렵지만, 원리는 간단합니다. "고속도로와 국도"입니다.
데이터가 100만 개가 있다고 칩시다. 가장 무식한 방법(Flat Search)은 100만 개를 내 쿼리 벡터와 일일이 비교하는 겁니다. 당연히 느리죠.
HNSW는 데이터를 계층(Layer)으로 나눕니다.
- Layer 2 (고속도로): 데이터들이 듬성듬성 연결되어 있습니다. 여기서 대략적인 위치를 찾습니다. ("서울 쪽으로 가자")
- Layer 1 (지방도): 조금 더 촘촘합니다. ("강남구 쪽으로 가자")
- Layer 0 (국도): 모든 데이터가 연결되어 있습니다. ("역삼동 123번지로 가자")
검색할 때 고속도로를 타고 순식간에 목적지 근처로 이동한 다음, 국도에 내려와서 정밀 탐색을 합니다. 이 덕분에 100만 개 중 몇백 개만 비교해도 정답을 찾을 수 있습니다.
6. 실제 - 주요 벡터 DB 사용법
1. Pinecone (가장 대중적인 관리형 서비스)
설치나 서버 관리가 필요 없습니다. API 키만 있으면 됩니다.
from pinecone import Pinecone
# 1. 초기화
pc = Pinecone(api_key="your-api-key")
# 2. 인덱스 생성 (한 번만 실행)
pc.create_index(
name="my-index",
dimension=1536, # OpenAI 임베딩 차원
metric="cosine" # 유사도 메트릭
)
# 3. 데이터 넣기 (Upsert)
index = pc.Index("my-index")
index.upsert([
("id1", [0.1, 0.2, ...], {"text": "맛있는 사과"}),
("id2", [0.3, 0.4, ...], {"text": "빨간 과일"})
])
# 4. 검색하기 (Query)
results = index.query(
vector=[0.15, 0.25, ...], # '사과'의 벡터
top_k=5,
include_metadata=True
)
for match in results.matches:
print(f"Score: {match.score}, Text: {match.metadata['text']}")
2. Chroma (오픈소스, 로컬 실행)
비용이 걱정되거나 로컬에서 테스트할 때 좋습니다.
import chromadb
client = chromadb.Client() # 메모리에서 실행
collection = client.create_collection("my_collection")
# 텍스트를 넣으면 자동으로 임베딩해줌 (편리함!)
collection.add(
documents=["이것은 사과다", "이것은 바나나다"],
ids=["id1", "id2"]
)
# 텍스트로 바로 검색
results = collection.query(
query_texts=["사과랑 비슷한 거 찾아줘"],
n_results=1
)
7. 팁 - 벡터 DB 도입 전 체크리스트
-
데이터가 몇 개인가?
- 10만 개 미만: 그냥 PostgreSQL(
pgvector)이나 로컬 라이브러리(FAISS) 써도 됩니다. 관리 포인트 늘리지 마세요. - 100만 개 이상: 무조건 전문 벡터 DB(Pinecone, Milvus) 쓰세요.
- 10만 개 미만: 그냥 PostgreSQL(
-
임베딩 차원은?
- 보통 OpenAI
text-embedding-3-small을 쓰면 1536차원입니다. - 오픈소스 모델(
all-MiniLM-L6-v2)은 384차원입니다. 차원이 작을수록 빠르고 쌉니다.
- 보통 OpenAI
-
메타데이터 필터링이 필요한가?
- "2024년 데이터 중에서만 찾아줘" 같은 요구사항이 있다면, 메타데이터 필터링 성능이 좋은 DB(Pinecone, Weaviate)를 선택해야 합니다.
8. 한 줄 요약
벡터 DB는 의미적 유사성 검색을 위해 고차원 벡터를 저장하고 고속으로 검색(HNSW)하는 데 특화된 데이터베이스입니다.
AI가 텍스트, 이미지, 오디오를 '이해'하게 만드는 핵심 저장소이며, RAG(검색 증강 생성) 시스템의 심장부입니다.
이제 SQL(SELECT *)의 시대를 넘어, 벡터(Find Similar)의 시대가 왔습니다.