pgvector는 PostgreSQL에서 벡터를 저장하고 유사도 검색을 하게 해 주는 확장(extension)이다. vector 타입 컬럼에 임베딩을 넣고, "이 벡터와 가장 가까운 행들"을 SQL로 뽑는다. 별도 벡터 DB를 세우지 않고 이미 쓰던 Postgres 안에서 RAG·의미검색·추천을 구현할 수 있다는 게 핵심 매력이다.
LLM 애플리케이션이 늘면서 "문장을 임베딩으로 바꿔 비슷한 걸 찾는" 수요가 폭발했다. 나도 사내 문서 검색을 만들 때 처음엔 전용 벡터 DB를 알아봤는데, 데이터가 이미 Postgres에 있으니 pgvector로 붙이는 게 운영이 훨씬 단순했다.
설치와 기본 사용
확장을 켜고 벡터 컬럼을 만든다. 차원 수는 쓰는 임베딩 모델에 맞춘다. 예를 들어 OpenAI text-embedding-3-small은 1536차원이다.
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE docs (
id bigserial PRIMARY KEY,
content text,
embedding vector(1536) -- 모델 차원과 일치해야 함
);
-- 검색: 코사인 거리로 가장 가까운 5개
SELECT id, content
FROM docs
ORDER BY embedding <=> $1 -- $1은 질의 임베딩
LIMIT 5;
여기서 <=>가 코사인 거리 연산자다. pgvector는 세 가지 거리를 지원한다. 어떤 걸 쓸지는 임베딩 모델이 어떤 거리로 학습됐는지에 맞춘다. 대부분의 텍스트 임베딩은 코사인이다.
| 연산자 | 거리 | 주 용도 |
|---|---|---|
<=> | 코사인 | 텍스트 임베딩 대부분 |
<-> | L2(유클리드) | 좌표·이미지 특징 |
<#> | 내적(음수) | 정규화된 벡터 랭킹 |
인덱스 없이는 느리다 — HNSW
인덱스가 없으면 pgvector는 모든 행과 거리를 다 계산한다. 수천 건이면 몰라도 수십만·수백만이면 못 버틴다. 그래서 근사 최근접 인덱스를 만든다. 요즘 실전 기본값은 HNSW다.
-- HNSW 인덱스 (거리 연산자에 맞는 클래스 지정)
CREATE INDEX ON docs
USING hnsw (embedding vector_cosine_ops);
-- 검색 정확도/속도 조절 (세션 단위)
SET hnsw.ef_search = 100; -- 클수록 정확·느림
HNSW는 그래프 기반이라 삽입은 조금 느리고 메모리를 더 먹지만, 검색 품질과 속도가 좋아 대부분 이걸 고른다. 예전엔 IVFFlat도 많이 썼는데, IVFFlat은 인덱스 만들기 전에 데이터가 충분히 있어야 하고 lists 파라미터 튜닝이 까다롭다. 새로 시작한다면 HNSW가 무난하다.
중요한 함정 하나. HNSW·IVFFlat은 근사 검색이라 진짜 최근접을 100% 보장하지 않는다. 정확도가 아쉬우면 ef_search를 올린다. 속도와 재현율(recall)의 트레이드오프라 실데이터로 몇 번 재보고 정하는 수밖에 없다.
실전 팁 — 하이브리드와 필터
순수 벡터 검색만으로는 고유명사·정확한 키워드에 약하다. 그래서 실무에선 Postgres의 전문검색(tsvector)과 벡터 검색을 섞는 하이브리드 검색을 많이 쓴다. 키워드 점수와 벡터 유사도를 각각 구해 가중 합치거나 RRF로 순위를 합친다. pgvector 하나로 두 검색을 같은 DB에서 돌릴 수 있는 게 강점이다.
메타데이터 필터도 자주 필요하다. "이 사용자의 문서 중에서만" 같은 조건이다. 이걸 WHERE user_id = $2로 붙이면 되지만, HNSW 인덱스와 필터가 부딪히면 필터를 만족하는 결과가 부족해질 수 있다. 이럴 땐 파티셔닝하거나, pgvector 최신 버전의 반복 스캔 옵션을 검토한다. 나는 사용자별 문서량 편차가 커서, 자주 검색되는 테넌트는 아예 테이블을 나눴다.
-- 필터 + 벡터 검색
SELECT id, content
FROM docs
WHERE user_id = $2
ORDER BY embedding <=> $1
LIMIT 5;
자주 묻는 질문
전용 벡터 DB 대신 pgvector를 써도 되나요?
데이터가 이미 Postgres에 있고 규모가 수백만 행 수준이라면 pgvector로 충분한 경우가 많습니다. 트랜잭션·조인·필터를 기존 SQL로 함께 쓸 수 있어 운영이 단순해집니다. 수억 벡터에 초저지연이 필요한 극단적 규모라면 전용 엔진을 검토하세요.
HNSW와 IVFFlat 중 뭘 골라야 하나요?
새로 시작한다면 HNSW를 권합니다. 검색 품질과 지연이 대체로 우수하고, IVFFlat처럼 인덱스 생성 전 데이터가 충분해야 한다는 제약이 없습니다. 대신 인덱스 빌드가 더 느리고 메모리를 더 쓰니, 삽입이 매우 잦고 메모리가 빠듯하면 IVFFlat도 고려합니다.
거리 연산자는 무엇을 써야 하나요?
임베딩 모델이 학습에 사용한 거리에 맞춥니다. 대부분의 텍스트 임베딩은 코사인 유사도 기준이라 <=>와 vector_cosine_ops를 씁니다. 인덱스의 연산자 클래스와 쿼리의 연산자가 일치해야 인덱스를 탄다는 점을 꼭 확인하세요.
검색 결과가 가끔 엉뚱한데 왜 그런가요?
HNSW·IVFFlat은 근사 검색이라 완벽한 최근접을 보장하지 않습니다. hnsw.ef_search를 올리면 재현율이 좋아지지만 느려집니다. 또 임베딩 모델·차원·정규화가 저장 시와 질의 시 동일한지 확인하세요. 이게 어긋나면 거리 자체가 무의미해집니다.

댓글 0