pgvector 실전 - PostgreSQL로 벡터 검색하기
RAG를 만들려면 임베딩 벡터를 어딘가에 저장하고, 질문 벡터와 가까운 것들을 빠르게 찾아야 합니다.
전용 벡터 DB(Pinecone, Qdrant 등)를 도입할 수도 있지만, 이미 쓰고 있는 PostgreSQL에 pgvector 확장만 얹으면 대부분의 실무 요구는 충분히 해결됩니다.
이 글에서는 pgvector 설치부터 컬럼 설계, 검색 쿼리, 인덱스 트레이드오프, 메타데이터 필터까지 SQL 위주로 정리합니다.
1. pgvector가 왜 매력적인가
벡터 검색만을 위해 새 인프라를 하나 더 운영하는 것은 부담입니다. pgvector는 그 부담을 없애줍니다.
- 기존 DB 그대로. 트랜잭션, 조인, 권한, 백업 전략을 이미 알고 있는 PostgreSQL 위에서 벡터를 다룹니다.
- 관계형 데이터와 한 몸. 문서 메타데이터(작성일, 카테고리, 소유자)와 벡터를 같은 테이블에 두고
WHERE로 필터할 수 있습니다. - SQL 그대로. 새 쿼리 언어를 배울 필요 없이
ORDER BY ... LIMIT으로 유사도 검색을 합니다.
비유하자면, 벡터 검색을 위해 별도 창고를 짓는 대신 쓰던 서랍장에 새 칸을 하나 추가하는 셈입니다.
2. 설치와 확장 활성화
pgvector는 PostgreSQL 확장입니다. 서버에 확장이 설치돼 있다면 DB에서 한 줄로 켭니다.
CREATE EXTENSION IF NOT EXISTS vector;
도커로 빠르게 띄운다면 공식 이미지를 쓰면 됩니다.
docker run -d \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
pgvector/pgvector:pg16
버전 확인:
SELECT extversion FROM pg_extension WHERE extname = 'vector';
3. vector 컬럼과 차원
vector 타입은 차원 수를 고정해서 선언합니다. 이 차원은 사용하는 임베딩 모델의 출력 차원과 반드시 일치해야 합니다. (예: OpenAI text-embedding-3-small은 1536차원)
CREATE TABLE doc_chunks (
id bigserial PRIMARY KEY,
document_id bigint NOT NULL,
content text NOT NULL,
category text, -- 메타데이터 필터용
created_at timestamptz NOT NULL DEFAULT now(),
embedding vector(1536) -- 임베딩 차원
);
차원이 안 맞는 벡터를 넣으면 삽입 자체가 실패합니다. 그리고 컬럼 재정의 + 전체 재색인이 필요합니다. 모델 선택은 초기에 신중히 하세요.