pgvector 인덱싱

pgvector 인덱싱 (HNSW / IVFFlat)

기본적으로 pgvector는 정확한 최근접 이웃 검색을 해서 완벽한 재현율(recall)을 보장해요. 데이터가 커지면 속도를 올리기 위해 근사 인덱스를 추가하는데, 이때 재현율 일부를 속도와 맞바꾸게 돼요. 근사 인덱스를 추가하면 쿼리 결과가 조금 달라질 수 있다는 점만 알아두면 돼요.

출처: https://github.com/pgvector/pgvector#indexing

HNSW 인덱스

HNSW는 다층 그래프를 만드는 인덱스예요. IVFFlat보다 속도-재현율 트레이드오프가 좋지만 빌드가 느리고 메모리를 더 써요. 또한 IVFFlat처럼 학습 단계가 없어서 테이블에 데이터가 없어도 인덱스를 만들 수 있는 게 장점이에요.

사용하려는 거리 함수마다 인덱스를 하나씩 만들어야 해요. L2 거리 기준이라면 이렇게 해요.

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);

halfvec에는 halfvec_l2_ops, sparsevec에는 sparsevec_l2_ops를 쓰면 되고, 다른 거리 함수도 비슷하게 뒤의 ops만 바꾸면 돼요.

  • 내적: vector_ip_ops
  • 코사인: vector_cosine_ops
  • L1: vector_l1_ops
  • 해밍: bit_hamming_ops
  • 자카드: bit_jaccard_ops

인덱스 옵션과 쿼리 옵션

HNSW 파라미터로 m(레이어당 최대 연결 수, 기본 16)과 ef_construction(그래프 생성용 후보 목록 크기, 기본 64)을 지정할 수 있어요. ef_construction을 높이면 재현율은 좋아지지만 인덱스 빌드/삽입 속도는 느려져요.

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);

검색 시 후보 목록 크기인 hnsw.ef_search를 조정해서 재현율을 높일 수 있어요. 기본 40이고, 높을수록 재현율은 좋아지고 속도는 느려져요.

SET hnsw.ef_search = 100;

단일 쿼리에만 적용하려면 트랜잭션 안에서 SET LOCAL을 쓰면 돼요.

BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT ...
COMMIT;

IVFFlat 인덱스

IVFFlat은 벡터를 여러 리스트로 나누고, 쿼리 벡터와 가장 가까운 리스트 일부만 검색하는 인덱스예요. HNSW보다 빌드가 빠르고 메모리를 덜 쓰지만 쿼리 성능은 상대적으로 낮아요. 좋은 재현율을 얻는 세 가지 요점이 있어요.

  1. 테이블에 데이터가 어느 정도 찬 뒤에 인덱스를 만든다.
  2. 리스트 수를 잘 고른다. 100만 행까지는 rows / 1000, 그 이상은 sqrt(rows)에서 시작하는 게 좋아요.
  3. 쿼리할 때 probe 수를 적절히 지정한다. 시작값으로 sqrt(lists) 정도가 좋아요.

L2 거리 기준 IVFFlat 인덱스 예시예요.

CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);

쿼리 시 probe 수를 조정할 수 있어요. 기본 1이고, 높일수록 재현율은 좋아지고 속도는 느려져요. probe 수를 리스트 수만큼 올리면 사실상 정확 검색이 되고 이때는 플래너가 인덱스를 쓰지 않아요.

SET ivfflat.probes = 10;

인덱스 빌드 속도 올리기

그래프가 maintenance_work_mem에 들어갈 만큼 작으면 인덱스가 훨씬 빨리 만들어져요.

SET maintenance_work_mem = '8GB';

다른 인덱스 유형과 마찬가지로 초기 데이터 로드 후에 인덱스를 만드는 게 빨라요. 병렬 워커 수를 늘리는 것도 방법이에요.

SET max_parallel_maintenance_workers = 7; -- plus leader

워커가 많아지면 max_parallel_workers(기본 8)도 함께 늘려야 할 수 있어요.

인덱싱 진행 상황 확인

pg_stat_progress_create_index 뷰로 진행률을 확인할 수 있어요.

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;

더 알아보기