pgvector를 PostgreSQL 15 위에서 굴리는데, 일반 OLTP 테이블이랑 섞여 있다 보니 메모리 경합이 심하다. 한 인스턴스에서 같이 굴리는 게 맞나 싶은 순간들이 꽤 있다.
튜닝한 파라미터
-- 16 vCPU, 64GB RAM 기준
shared_buffers = 16GB
effective_cache_size = 48GB
work_mem = 128MB -- vector scan 중 sort 때문에 올림
maintenance_work_mem = 4GB -- HNSW build 때 엄청 먹음
max_parallel_workers_per_gather = 4
random_page_cost = 1.1 -- NVMe 기준
처음엔 work_mem 기본값(4MB)으로 하다가 vector ORDER BY에서 temp file 쓰는 게 찍혀서 올렸다. EXPLAIN (ANALYZE, BUFFERS) 찍어보면 Sort Method: external merge Disk: 580000kB 이러고 있음. 디스크 소트는 답이 없다.
파티셔닝
vector 테이블이 현재 190만 row인데 월별로 파티셔닝해서 핫/콜드 분리. 최근 3개월만 HNSW 인덱스 걸고 나머지는 IVFFlat. 쿼리는 union all로 합쳐 top-k 뽑음. 프론트 쿼리 latency p95가 110ms → 42ms.
뜻밖의 이슈
VACUUM이 HNSW 인덱스가 있는 테이블에서 이상하게 느리다. autovacuum_vacuum_cost_limit을 2000으로 올리니 따라잡긴 하는데, 이건 pg_cron으로 야간에 manual VACUUM ANALYZE 돌리는 게 편했다.
그리고 PG 15는 MERGE가 들어왔는데, upsert 패턴에서 INSERT ... ON CONFLICT보다 가독성 좋다. 다만 vector 업데이트 시엔 인덱스 재계산이 비싸서, 주기적 배치로 "지우고 다시 넣기" 전략이 오히려 빠른 경우도 있었음.
DB 한 대가 RAG 백엔드 + 서비스 OLTP 겸직하는 구조는 오래 못 갈 듯. 분리 계획을 세우는 중.