사전 필터와 사후 필터, 왜 둘 다 함정이 될 수 있나?
결론부터 말하면 둘 중 하나가 항상 우월하지 않다. 필터 통과율이 0.01% 미만으로 매우 선택적이면 사전 필터가 유리하고, 통과율이 높아질수록 사후 필터·결합형 탐색이 역전된다. 승부처는 평균 속도가 아니라 필터 선택도별 filtered recall@k와 p99 지연시간이다.
- 필터 선택도가 전체의 0.01% 미만일 때 사전 필터가 유리한 사례가 2025년 연구에서 관찰됐다 (arxiv.org/html/2603.23710v1)
- pgvector는 기본적으로 HNSW 탐색 후
WHERE조건을 적용하는 사후 필터 방식이라LIMIT 10에도 10개 미만이 반환될 수 있다 - pgvector 0.8.0의
iterative_scan은 부족한 결과를 보완하지만 탐색량이 늘어 CPU와 tail latency가 상승한다 - 전체 코퍼스 top-k를 정답으로 recall을 계산하면 필터 적용 후 recall이 왜곡된다 — 반드시 필터 통과 집합 기준 재계산이 필요하다
- 전용 벡터DB와 pgvector의 차이는 제품명이 아니라 필터를 탐색 전·중·후 어디서 거는지에 있다
두 방식은 구조부터 다르다. 사전 필터는 메타데이터 조건으로 후보를 먼저 줄인 뒤 그 줄어든 집합 안에서 최근접 탐색을 한다. 사후 필터는 HNSW 그래프에서 ef_search개의 후보를 먼저 찾고, 그중 조건에 맞지 않는 것을 버린다. 둘을 나란히 놓고 보면 "필터가 있으니 빨라진다"는 직관이 사후 필터에서는 성립하지 않는다는 점이 드러난다. 이미 그래프 탐색 비용을 다 치른 뒤에 버리는 구조이기 때문이다.
필터 선택도에 따라 실제로 무엇이 갈리는가?
선택도 구간마다 유리한 전략이 다르다. 2025년 연구는 선택도를 세 구간으로 나눠 설명한다. 매우 낮은 선택도(예: 전체의 0.01% 미만만 통과)에서는 메타데이터 인덱스로 먼저 후보를 좁힌 뒤 정확 검색하는 사전 필터가 유리할 가능성이 크다고 보고했다. 중간 선택도 구간에서는 사전 필터 비용과 HNSW 탐색 비용을 실제 데이터 분포로 직접 비교해야 한다는 점을 강조한다. 선택도가 높아 대부분의 행이 조건을 통과하는 경우에는 HNSW 탐색과 필터를 결합한 효율적 실행이나 일반적인 인덱스 기반 실행이 유리할 수 있다고 정리했다 (arxiv.org/html/2603.23710v1). 핵심은 이 세 구간 중 어디에 자신의 tenant_id, ACL, 언어·지역 조건이 위치하는지를 실측하지 않고는 전략을 고를 수 없다는 점이다. RAG 파이프라인에서 자주 쓰이는 테넌트·권한·버전 필터는 선택도가 데이터셋마다 크게 달라지므로, 고정된 공식으로 판단할 수 없다.
HNSW 탐색 뒤 필터를 버리면 결과는 어떻게 되는가?
조용히 결과가 부족해진다. pgvector에서 HNSW 인덱스와 WHERE 조건을 함께 쓰면 일반적으로 그래프 탐색이 끝난 뒤 조건을 적용하는 사후 필터가 된다. 필터 통과율이 낮으면 LIMIT 10을 요청해도 조건을 만족하는 행이 10개에 못 미친 채 반환될 수 있다 (github.com/pgvector/pgvector). 이 문제는 에러나 경고 없이 발생하기 때문에, 운영 중인 RAG 시스템에서 recall이 낮아진 원인을 찾을 때 가장 먼저 의심해야 할 지점이다. 특히 tenant_id, 문서 권한, 삭제 여부처럼 정답 집합 자체를 바꾸는 조건에서 이 현상이 두드러진다. 필터 후 결과를 기준으로 recall@k를 다시 계산하지 않고 전체 코퍼스의 top-k를 정답으로 삼으면, 실제로는 멀쩡한 시스템이 recall이 낮은 것처럼 보이거나 반대로 문제를 놓칠 수 있다.
iterative_scan은 사후 필터의 함정을 얼마나 메우는가?
부분적으로 메우지만 비용이 따른다. pgvector 0.8.0은 필터로 결과가 부족할 때 HNSW 탐색을 계속하는 iterative_scan을 도입했다. hnsw.iterative_scan = relaxed_order는 성능을 우선하고 strict_order는 거리 순서 보장을 우선하며, 탐색량은 hnsw.max_scan_tuples로 제한한다 (github.com/pgvector/pgvector). 2026년 기준 pgvector 0.8.0 계열 문서에서 명시하듯, 이 기능은 부족한 결과를 보완하는 대신 더 많은 그래프 노드를 탐색하므로 CPU 사용량과 특히 p95·p99 지연시간이 늘어날 수 있다. 즉 "필터가 선택적일수록 검색이 빨라진다"는 가정은 사후 필터 구조에서는 성립하지 않고, 오히려 결과 부족을 막으려 탐색을 반복할수록 지연시간이 증가하는 역방향 관계가 나타난다. ef_search와 max_scan_tuples를 올릴수록 이 경향은 더 뚜렷해진다.
필터 전용 인덱스를 먼저 깔면 비용은 얼마나 드는가?
공짜가 아니다. 사전 필터를 구현하려면 tenant_id, 권한, 날짜, 문서 유형 같은 컬럼에 B-tree, GIN, bitmap 또는 payload 인덱스를 추가로 걸어야 한다. 이 인덱스들은 저장공간을 늘리고, 쓰기 증폭을 유발하며, VACUUM과 업데이트 비용을 추가한다. "recall이 올라간다"는 이유만으로 도입하면 쓰기 경로 성능을 따로 검증하지 않은 채 운영에 들어가는 셈이다. 특히 삭제 여부·최신 버전 여부처럼 쓰기 빈도가 높은 컬럼에 인덱스를 걸면, 읽기 recall 개선과 쓰기 지연 증가 사이에서 트레이드오프가 생긴다. 이 비용은 필터 선택도와 무관하게 발생하므로, 사전 필터 도입 여부를 결정할 때는 읽기 쪽 recall 이득만이 아니라 쓰기 경로 비용까지 같은 저울에 올려야 한다.
실측 비교표로 보면 무엇이 드러나는가?
표로 정리하면 전략별 강점이 선택도 구간에 묶여 있다는 점이 분명해진다.
| 필터 선택도 | 유리한 전략 | 주요 리스크 |
|---|---|---|
| 0.01% 미만 | 사전 필터(메타데이터 인덱스 선행) | 필터 전용 인덱스의 쓰기·저장 비용 |
| 0.1~1% | 사전/사후 혼합, 데이터별 실측 필요 | 일반화된 공식 부재, 재벤치마크 필수 |
| 10% 이상 | HNSW·필터 결합 실행 또는 사후 필터+iterative_scan | tail latency 급증 가능 |
이 표는 고정된 수치가 아니라 판단 구조다. 실제 비교에는 필터 통과율, filtered recall@k, 요청 k 대비 반환 행 수, p50·p95·p99 지연시간, QPS, 인덱스 생성 시간과 크기, 업데이트·삭제 후 성능 변화를 함께 기록해야 한다 (percona.com/blog/benchmarking-vector-indexes). 벡터 차원, HNSW의 M·ef_construction·ef_search, pgvector·PostgreSQL 버전, 캐시 상태 중 하나만 달라도 같은 코드로 전혀 다른 숫자가 나온다.
전략을 바꾸려면 실제로 무엇을 건드려야 하는가?
사전 필터와 사후 필터 사이를 오가는 일은 쿼리 한 줄 수정이 아니다. 사후 필터에서 사전 필터로 바꾸려면 필터 컬럼에 새 인덱스를 설계하고 쓰기 경로 성능을 재검증해야 한다. 반대로 사후 필터에 iterative_scan을 켜는 쪽으로 가려면 hnsw.ef_search, hnsw.max_scan_tuples 값을 조정하면서 p99 지연시간이 SLA를 넘지 않는지 확인해야 한다. 어느 방향이든 기존에 전체 코퍼스 기준으로 측정해둔 recall@k 벤치마크는 무효가 된다. 필터 통과 집합을 brute-force로 다시 계산한 filtered recall@k로 베이스라인을 새로 세워야 비교가 성립한다. 이 재계산 작업과 재벤치마크 비용은 전략 전환의 실질적인 진입 장벽이다.
어느 쪽도 정답이 아닌 경우는 언제인가?
필터가 중간 선택도(대략 0.1~10% 구간)에 있고 데이터 규모가 커질 때다. 이 구간에서는 연구도 "사전 필터와 inline/iterative 필터를 상호 보완적인 전략으로 봐야 한다"고 설명할 뿐, 고정된 우열을 제시하지 않는다 (arxiv.org/html/2603.23710v1). pgvector는 기존 PostgreSQL 트랜잭션·조인이 중요하고, 데이터 규모가 운영 범위 안에 있으며, 테넌트·권한 필터가 이미 SQL에 존재하는 환경에서 적합하다. 반대로 필터가 매우 선택적이면서 요청량이 크고, 수천만~수억 벡터 규모에서 높은 동시성과 엄격한 p99 목표가 필요하다면 필터 결합형 실행기를 갖춘 전용 벡터DB 검토가 필요하다. 이 판단은 제품 선택이 아니라, 자신의 필터 선택도와 규모를 먼저 측정한 뒤에만 내릴 수 있다.
핵심 정리
- 사전 필터는 선택도 0.01% 미만처럼 극히 선택적인 조건에서 유리할 가능성이 크다
- pgvector의 기본 동작은 사후 필터이며
LIMIT k보다 적은 결과가 조용히 반환될 수 있다 iterative_scan은 결과 부족을 보완하지만 CPU·p95·p99 지연시간 증가라는 비용을 수반한다- recall 비교는 반드시 필터 통과 집합 기준 filtered recall@k로 재계산해야 하며, 전체 코퍼스 top-k 기준은 왜곡을 낳는다
- 전용 벡터DB 대 pgvector의 핵심 차이는 브랜드가 아니라 필터를 탐색 전·중·후 어디서 거는지다
자주 묻는 질문
pgvector에서 필터 조건 컬럼에 일반 인덱스를 걸면 사전 필터가 자동으로 되는가?
아니다. HNSW와 WHERE를 함께 쓰면 기본은 여전히 그래프 탐색 후 필터를 적용하는 사후 필터 구조다. 사전 필터 효과를 보려면 쿼리 플래너가 필터 인덱스를 먼저 쓰도록 유도하는 별도 설계나 iterative_scan 설정 조정이 필요하다.
iterative_scan의 relaxed_order와 strict_order 중 어떤 것을 기본값으로 둬야 하나?
고정된 기본값은 없다. relaxed_order는 지연시간을 줄이는 대신 거리 순서가 완벽하지 않을 수 있고, strict_order는 순서를 보장하는 대신 탐색 비용이 늘어난다. 선택은 recall·순서 정확도와 p99 지연시간 중 무엇을 우선할지에 달려 있다.
filtered recall@k를 측정할 때 brute-force 비교가 왜 꼭 필요한가?
필터 조건은 정답 집합 자체를 바꾸기 때문이다. 전체 코퍼스에서 뽑은 top-k를 기준으로 삼으면 필터를 통과하지 못하는 정답이 섞여 recall이 실제보다 낮게 혹은 높게 보일 수 있다. 필터 통과 집합 안에서 다시 brute-force로 정답을 구해야 비교가 성립한다.
중간 선택도 구간에서 벤치마크를 어떻게 설계해야 하나?
필터 통과율을 0.01%, 0.1%, 1%, 10%, 100% 구간으로 나눠 각각 filtered recall@k, p50·p95·p99 지연시간, 반환 행 수를 따로 기록해야 한다. 벡터 차원, HNSW 파라미터, 캐시 상태까지 함께 기록하지 않으면 같은 pgvector 버전이라도 결과를 재현할 수 없다.
더 알아보기
벡터 검색 설계의 핵심 판단축: 재현율, 지연, 비용의 균형 — 이 주제의 종합 가이드