ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • pgvector에서 LIMIT 10인데 결과가 적은 이유: HNSW와 필터 순서
    AI Agent 2026. 9. 11. 20:29
    728x90
    반응형

    일부만 구슬로 채워진 점토 판과 망 뒤의 구슬

    일부만 구슬로 채워진 점토 판과 망 뒤의 구슬. 이해를 돕기 위한 AI 생성 개념 이미지입니다.

    조건에 맞는 문서가 충분한데 pgvector 검색이 LIMIT 10보다 적은 행을 반환할 수 있습니다. SQL의 LIMIT는 열 개를 보장하는 값이 아니라 최대값이고, 근사 인덱스가 후보를 찾은 뒤 WHERE 필터를 적용하면 후보 대부분이 탈락할 수 있습니다.

    pgvector 공식 README는 현재 HNSW의 기본 hnsw.ef_search를 40으로 설명하고, 근사 인덱스에서는 필터가 인덱스 스캔 뒤 적용된다고 설명합니다. 이 글의 버전·기본값은 2026년 9월 7일 확인한 문서 기준이며, 설치한 extension과 PostgreSQL 버전이 다르면 실제 계획과 옵션을 다시 확인해야 합니다.

    필터를 지우기 전에 정확 검색과 근사 검색의 개수, 실행 계획, 후보 탐색 설정을 비교해야 합니다.

    먼저 SQL 자체의 개수를 확인합니다

    가상의 다국어 문서 테이블을 보겠습니다.

    SELECT id, title
    FROM documents
    WHERE tenant_id = 42
      AND language = 'ko'
    ORDER BY embedding <=> $1
    LIMIT 10;

    첫 질문은 tenant와 language 조건에 맞는 행이 실제로 열 개 이상인지입니다.

    SELECT count(*)
    FROM documents
    WHERE tenant_id = 42
      AND language = 'ko';

    여기서 열 개 미만이면 벡터 인덱스 문제가 아닙니다. 충분하다면 정확 검색과 근사 인덱스 경로를 비교합니다. 운영 쿼리의 권한 필터를 지워 개수만 맞추는 것은 해결책이 아닙니다.

    근사 검색 뒤 필터가 적용될 수 있습니다

    pgvector 공식 문서는 근사 인덱스 사용 시 필터가 인덱스 탐색 뒤 적용돼 결과가 적을 수 있다고 설명합니다. README의 설명처럼 조건에 맞는 행이 전체의 10%이고 HNSW의 기본 hnsw.ef_search가 40이면 평균적으로 네 행만 조건을 통과할 수 있다는 식입니다.

    전체에서 가까운 후보를 먼저 탐색했는데 tenant 42의 한국어 문서가 네 개뿐이라면 네 개만 남습니다. 데이터베이스 전체에 조건을 만족하는 행이 많아도 첫 후보 집합에 충분히 포함되지 않을 수 있습니다. 10%, 40, 4는 공식 문서의 원리 설명과 이 글의 가상 시나리오를 섞은 값이며, 특정 데이터베이스에서 항상 그 수가 나온다는 뜻은 아닙니다. dead tuple과 실제 필터 분포도 결과를 바꿉니다.

    EXPLAIN으로 실제 경로를 봅니다

    PostgreSQL EXPLAIN 문서에 따라 실행 계획을 확인합니다.

    EXPLAIN (ANALYZE, BUFFERS)
    SELECT id
    FROM documents
    WHERE tenant_id = 42
      AND language = 'ko'
    ORDER BY embedding <=> $1
    LIMIT 10;

    ANALYZE는 쿼리를 실제 실행하므로 수정 쿼리나 운영 부하에서는 주의해야 합니다. SELECT라도 큰 테이블에서 비용이 생길 수 있습니다.

    확인할 항목은 사용한 인덱스, 읽은 행과 필터로 제거된 행, 예상·실제 행 수, buffer 사용입니다. 통계가 부정확하면 planner 선택도 달라질 수 있습니다.

    정확 검색을 진단 기준으로 둡니다

    근사 인덱스를 사용하지 않는 비교 경로에서 같은 필터와 거리 연산을 실행해 결과 수와 ID를 저장합니다. 정확 검색 결과가 열 개인데 HNSW만 적다면 후보 탐색 문제를 의심할 수 있습니다. 두 경로 모두 적다면 데이터·필터·파라미터를 먼저 봅니다.

    정확 검색은 큰 테이블에서 비쌀 수 있으므로 공개·제한 샘플이나 작은 tenant에서 진단합니다. 필터 컬럼 인덱스를 이용한 정확 경로와 순차 스캔 경로가 다를 수 있으므로, 'HNSW를 껐다'는 말만으로 계획을 가정하지 말고 EXPLAIN 결과를 기록합니다. 운영 전체 스캔을 무턱대고 실행하지 않습니다.

    iterative scan과 후보 폭을 조정합니다

    pgvector 공식 문서는 0.8.0부터 iterative index scan을 지원한다고 설명합니다. 필터 뒤 결과가 부족할 때 설정된 한도 안에서 추가 탐색합니다.

    BEGIN;
    SET LOCAL hnsw.iterative_scan = strict_order;
    SET LOCAL hnsw.ef_search = 100;
    -- 필요할 때만 한도를 조정하고 실제 계획·지연을 다시 측정합니다.
    SET LOCAL hnsw.max_scan_tuples = 40000; -- 예시 값, 실제로는 계획·지연을 비교
    SELECT id, title
    FROM documents
    WHERE tenant_id = 42 AND language = 'ko'
    ORDER BY embedding <=> $1
    LIMIT 10;
    COMMIT;

    strict_order는 거리순을 엄격하게 유지하려는 iterative scan 모드입니다. relaxed_order는 recall에 유리할 수 있지만 거리순이 약간 흐트러질 수 있어, 공식 예시처럼 materialized CTE 바깥에서 다시 정렬하는 후처리가 필요할 수 있습니다. hnsw.max_scan_tuples는 iterative scan이 방문할 최대 tuple 수를 제한하지만 초기 스캔에는 영향을 주지 않는 근사적인 한도입니다. 세션·트랜잭션 범위와 설치 버전을 확인해야 하며, 어느 옵션도 정확 검색으로 바꾸는 스위치는 아닙니다. 탐색 폭과 스캔 한도를 늘리면 recall이 나아질 수 있지만 지연과 자원 사용도 늘 수 있습니다.

    필터 컬럼 인덱스와 데이터 분포도 봅니다

    필터 선택도가 높다면 tenant_id나 category에 일반 B-tree 인덱스를 두는 것이 도움이 될 수 있습니다. pgvector 문서도 filter column 인덱스가 많은 경우 빠른 정확 최근접 검색을 제공할 수 있다고 설명합니다. 필터 값이 몇 개뿐이면 partial index, 값이 매우 많거나 tenant 격리가 중요하면 partitioning 또는 별도 테이블을 검토할 수 있습니다. 여러 tenant가 하나의 근사 인덱스를 공유하면 다른 tenant의 벡터가 recall과 속도에 영향을 줄 수 있습니다.

    그러나 인덱스를 늘리면 쓰기 비용과 운영 복잡도가 생기고, partial index는 조건별 인덱스를 관리해야 합니다. 실제 계획과 workload로 판단해야 합니다.

    재현 가능한 비교표를 남깁니다

    항목기록 값
    pgvector/PostgreSQL 버전실제 서버 출력
    쿼리·필터파라미터 포함 템플릿
    조건 일치 행 수count 결과
    정확 검색 상위 ID기준 집합
    HNSW 상위 ID비교 집합
    반환 개수·겹침count와 recall 후보
    실행 계획EXPLAIN 결과
    지연warm/cold 조건 구분

    샘플 한 개가 아니라 여러 질의와 선택도에서 비교해야 합니다. 한 쿼리의 열 개 일치만으로 전체 검색 품질을 보증하지 않습니다.

    정리

    LIMIT 10은 열 개를 채우라는 명령이 아닙니다. HNSW 후보 뒤 필터가 적용되면 충분한 데이터가 있어도 결과가 적을 수 있습니다. 정확 검색과 count, EXPLAIN을 기준으로 원인을 나눈 뒤 iterative scan, ef_search·스캔 한도, 필터 인덱스와 tenant 분할을 검토해야 합니다. 결과 개수가 늘어도 그것만으로 recall이나 검색 품질이 보증되는 것은 아니므로, 기준 집합과 겹침·순서·지연을 함께 기록해야 합니다.

    자료 확인일은 2026년 9월 7일입니다. SQL과 수치는 설명용이며 실제 데이터베이스에서 실행하거나 성능을 측정하지 않았습니다.

    728x90
    반응형
Designed by Tistory.