pgvector 심화: HNSW 튜닝, 필터링, 대규모 운영
PostgreSQL 안에 벡터 검색을 넣는다는 것
pgvector는 PostgreSQL 확장(extension)이다. CREATE EXTENSION vector 한 줄로 활성화하면 기존 테이블에 vector 컬럼을 추가하고, HNSW 또는 IVFFlat 인덱스를 붙여 ANN 검색을 수행할 수 있다. 별도의 벡터 DB를 추가하지 않아도 SQL JOIN, 트랜잭션, 접근 제어, 백업이 그대로 동작한다.
이 단순함이 매력이지만, 운영 규모가 커지면 함정이 나타난다. HNSW 인덱스는 메모리를 많이 요구하고, 필터링 쿼리는 의도치 않게 recall이 낮아지며, 수천만 벡터 이상에서는 인덱스 빌드 자체가 수 시간이 걸린다. 이 챕터는 이런 문제들을 파라미터 수준에서 다룬다.
HNSW 파라미터 심화
pgvector의 HNSW 인덱스는 세 가지 파라미터로 동작을 제어한다.
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);파라미터 역할과 트레이드오프
| 파라미터 | 적용 시점 | 기본값 | 효과 |
|---|---|---|---|
m | 인덱스 빌드 | 16 | 노드당 최대 연결 수. 높을수록 recall↑, 인덱스 크기↑, 빌드 시간↑ |
ef_construction | 인덱스 빌드 | 64 | 빌드 시 후보 탐색 크기. 높을수록 그래프 품질↑, 빌드 시간↑ |
hnsw.ef_search | 쿼리 실행 | 40 | 탐색 시 후보 리스트 크기. 높을수록 recall↑, 쿼리 지연↑ |
ef_search는 세션 수준에서 바꿀 수 있다.
SET hnsw.ef_search = 100;
SELECT * FROM items ORDER BY embedding <=> '[...]' LIMIT 10;튜닝 기준선
| 워크로드 | m | ef_construction | ef_search |
|---|---|---|---|
| 빠른 검색, recall 최소 요건 | 16 | 64 | 40 |
| 일반 프로덕션 | 16 | 128 | 80 |
| 고품질 검색 우선 | 32 | 200 | 200 |
| 메모리 절약 | 8 | 64 | 40 |
m과 ef_construction은 인덱스를 재생성하지 않으면 바꿀 수 없다. 프로덕션 전에 representative dataset으로 recall을 측정한 뒤 고정하는 것이 핵심이다.
HNSW vs IVFFlat: 언제 무엇을 선택하는가
pgvector는 두 가지 인덱스 타입을 지원한다.
IVFFlat은 CREATE INDEX 전에 데이터가 충분히 들어 있어야 군집화가 의미 있다. 권장: lists = 데이터가 1M 이하일 때 rows/1000, 1M 초과일 때 sqrt(rows).
-- 인덱스 생성 후 쿼리 시 탐색할 클러스터 수
SET ivfflat.probes = 20;대부분의 신규 서비스는 HNSW를 기본으로 선택하고, IVFFlat은 메모리가 명확히 제약될 때만 고려한다.
필터링과 Iterative Scan
벡터 검색에서 필터링은 까다로운 문제다. "이 사용자의 문서 중에서 가장 유사한 것을 찾아라"처럼 메타데이터 조건이 결합되면 recall이 의도치 않게 낮아진다.
문제: 필터링이 recall을 무너뜨리는 원리
ef_search = 40인 HNSW, 전체 벡터의 10%만 필터 조건 만족
→ 그래프 탐색 후보 40개 중 조건 만족: 평균 4개
→ TOP 10을 요청했는데 4개밖에 반환되지 않음
→ 실제 관련 벡터를 놓침 (silent recall 저하)pgvector 0.8.0: Iterative Scan
pgvector 0.8.0은 이 문제를 해결하는 iterative scan을 도입했다. 필터 조건을 만족하는 결과가 요청 수(limit)에 미치지 못하면 인덱스 탐색 범위를 자동으로 확장한다.
-- 필터 조건이 있는 쿼리에 iterative scan 적용
SET hnsw.iterative_scan = strict_order;
-- 또는
SET hnsw.iterative_scan = relaxed_order;
SELECT id, content
FROM docs
WHERE user_id = 42
ORDER BY embedding <=> '[...]' LIMIT 10;| 모드 | 동작 | 적합한 경우 |
|---|---|---|
strict_order | 거리 순서를 엄격히 유지하며 확장 | 정확한 상위 k 순서가 필요할 때 |
relaxed_order | 순서를 다소 완화해 더 빠른 탐색 | 순서보다 recall이 중요할 때 |
제어 파라미터:
-- 최대 방문 튜플 수 (기본: 20000)
SET hnsw.max_scan_tuples = 50000;
-- 메모리 사용량을 work_mem의 N배로 제한
SET hnsw.scan_mem_multiplier = 2;양자화 전략: 메모리 절감
대규모 배포에서 메모리는 가장 큰 비용 요인이다. pgvector 0.7.0부터 여러 양자화 방법을 지원한다.
halfvec: 반정밀도 저장
-- 768차원 halfvec HNSW 인덱스
CREATE INDEX ON items USING hnsw ((embedding::halfvec(768)) halfvec_l2_ops);
-- 또는 컬럼을 halfvec으로 정의
ALTER TABLE items ADD COLUMN embedding_half halfvec(768);- float32 → float16 변환: 4바이트 → 2바이트 (50% 절감)
- 최대 4,000차원 지원
- 대부분의 임베딩 모델에서 recall 저하 미미 (< 1%p)
이진 양자화: 극단적 압축
-- 이진 양자화 후 hamming 거리로 ANN
CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(1536)) bit_hamming_ops);
-- 쿼리: 이진 인덱스로 후보 추출 후 원본 벡터로 재순위 (re-rank)
SELECT id, embedding <=> query_vec AS score
FROM items
WHERE id = ANY(
SELECT id FROM items
ORDER BY binary_quantize(embedding) <~> binary_quantize(query_vec)
LIMIT 100
)
ORDER BY score
LIMIT 10;- float32 → 1비트: 32배 이상 압축
- 최대 64,000차원 지원
- recall 손실이 크므로 반드시 원본 벡터로 재순위(re-ranking) 필요
- OpenAI
text-embedding-3-*모델은 이진 양자화 후 재순위에서도 높은 정확도 유지
메모리 요구량 비교 (10M × 1536d 기준)
| 방식 | 벡터 크기 | 인덱스 예상 크기 |
|---|---|---|
| float32 (기본) | 약 58GB | 80~120GB |
| halfvec (float16) | 약 29GB | 40~60GB |
| 이진 양자화 | 약 1.8GB | 2~5GB (단, 원본 벡터 별도 유지) |
대규모 인덱스 빌드: 메모리와 병렬화
HNSW 인덱스는 빌드 시 그래프 전체를 메모리에 올려야 한다. maintenance_work_mem이 부족하면 디스크 기반 빌드로 폴백되어 10~50배 느려진다.
-- 인덱스 빌드 전 세션에서 설정
SET maintenance_work_mem = '8GB';
-- 병렬 빌드 (worker 수 + 리더 1개)
SET max_parallel_maintenance_workers = 7;
-- max_parallel_workers가 기본 8이므로 맞춰 올릴 것
ALTER SYSTEM SET max_parallel_workers = 16;
SELECT pg_reload_conf();메모리 부족 징후: 인덱스 빌드 로그에 다음 메시지가 뜨면 디스크 폴백 중이다.
NOTICE: hnsw graph size (4096 MB) exceeds maintenance_work_mem (1024 MB)
DETAIL: Building on disk and reading from disk.빌드 전략: 데이터를 먼저, 인덱스는 나중에
-- 1. 인덱스 없이 대량 삽입
COPY items (id, embedding) FROM '/data/vectors.csv' CSV;
-- 2. 삽입 완료 후 인덱스 생성
SET maintenance_work_mem = '16GB';
CREATE INDEX CONCURRENTLY ON items USING hnsw (embedding vector_cosine_ops);CREATE INDEX CONCURRENTLY는 테이블 잠금 없이 빌드하지만, 빌드 시간이 1.5~2배 더 걸린다.
인덱스 크기 추정
HNSW 인덱스 크기 ≈ N × (D × 4) × 1.5 ~ 2.0
(N: 벡터 수, D: 차원, 1.5~2.0: 그래프 오버헤드 계수)
예: 10M × 768d = 10,000,000 × 768 × 4 × 1.7 ≈ 52GB
예: 10M × 1536d ≈ 104GBEXPLAIN으로 벡터 쿼리 분석
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM items
ORDER BY embedding <=> '[...]' LIMIT 10;출력에서 주목할 항목:
| 항목 | 의미 |
|---|---|
Index Scan using hnsw_idx | HNSW 인덱스 사용 중 |
Buffers: shared hit=N, read=M | hit: 캐시, read: 디스크 IO. read↑ = 인덱스 캐시 미스 |
Seq Scan | 인덱스 미사용. 필터 조건이 너무 선택적이거나 인덱스 누락 |
Parallel Seq Scan | 병렬 전체 스캔. 소규모에서는 합리적 |
인덱스를 생성했는데도 Seq Scan이 나타나면:
-- 인덱스 스캔 강제 (테스트 용도)
SET enable_seqscan = off;운영 패턴과 체크리스트
인덱스 건강 모니터링
-- 인덱스 크기 확인
SELECT pg_size_pretty(pg_relation_size('hnsw_idx'));
-- 인덱스 사용 통계
SELECT idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
WHERE indexrelname = 'hnsw_idx';빈번한 문제와 대응
| 문제 | 증상 | 대응 |
|---|---|---|
| recall 저하 | 검색 품질 불만, 평가 지표 하락 | ef_search↑, iterative_scan 활성화 |
| 인덱스 빌드 지연 | 수 시간 이상 소요 | maintenance_work_mem↑, 병렬 worker↑ |
| 쿼리 지연 급등 | 캐시 미스 후 P99 급등 | shared_buffers↑, 인덱스 크기 줄이기(halfvec) |
| 필터 후 결과 부족 | LIMIT 미달 반환 | iterative_scan 활성화, max_scan_tuples 조정 |
| 대용량 테이블 VACUUM 차단 | autovacuum 중 인덱스 참조 오류 | VACUUM ANALYZE 주기 조정, dead tuple 비율 감시 |
pgvector 적합/부적합 판단 기준
pgvector 계속 유지 기준:
✓ 벡터 수 < 1,000만
✓ 기존 PostgreSQL 인프라와 JOIN이 핵심
✓ 필터 조건의 선택률 > 10%
✓ 인덱스가 available RAM(shared_buffers)에 들어감
전용 벡터 DB(Qdrant, Milvus) 전환 고려 기준:
✗ 벡터 수 > 5,000만
✗ 인덱스가 메모리를 초과해 지속적인 cache miss
✗ 필터 조건 선택률 < 1% + 초저지연 요구
✗ 수평 스케일아웃이 필수References
- pgvector GitHub README — Parameters, Quantization, Filtering
- pgvector 0.8.0 Released — PostgreSQL.org
- Supercharging vector search with pgvector 0.8.0 on Aurora PostgreSQL — AWS Blog
- Announcing pgvector 0.8.0 — The Nile
- Scaling pgvector: Memory, Quantization, and Index Build Strategies — DEV.to
- How to scale vector search in Postgres for RAG and AI agents — ClickHouse Engineering
- HNSW Index Memory Formula — pgvector Issue #769
- pgvector DBA Guide Part 2: Indexes (March 2026 update) — dbi-services
- Filtering — pgEdge pgvector Documentation