pgvector in Production: HNSW, Filtered Search, Quantization, and Recall You Actually Measure
A practical guide to taking PostgreSQL vector search from a 5,000-row prototype to millions of rows without surprises: choosing the right column type (vector, halfvec, bit) and what each one costs in disk, RAM and TOAST fetches; HNSW versus IVFFlat internals and the three parameters that actually matter (m, ef_construction and hnsw.ef_search); building the index with maintenance_work_mem and parallel workers, and why CREATE INDEX CONCURRENTLY disables them; measuring real recall@K against an exact scan with a Python script you can run in CI, instead of trusting library defaults; the silent failure of combining WHERE filters with approximate search and how pgvector 0.8 iterative scans fix it (strict_order, relaxed_order, max_scan_tuples and scan_mem_multiplier); partial indexes for permanent filters and hash partitioning for multi-tenant workloads; halfvec quantization to halve the index and binary quantization with two-phase reranking to shrink it 32x; the exact conditions under which a query uses the ANN index and the ones that silently drop it to a Seq Scan; and long-term operations: graph degradation under churn, REINDEX CONCURRENTLY, pg_prewarm, and SET LOCAL versus SET with connection pools and PgBouncer. Includes eight recurring mistakes, a production checklist and an FAQ. With production-ready SQL and Python. Expanded edition: the memory budget and the arithmetic that decides your machine before any parameter does (shared_buffers, effective_cache_size and pg_prewarm), hybrid search with tsvector and Reciprocal Rank Fusion for the code and proper-noun queries vectors never find, changing embedding models without downtime using expand-contract and batched backfills, what writes do to the HNSW graph (tombstones, per-table autovacuum and when REINDEX CONCURRENTLY is due), three-layer observability with a production recall canary and pg_stat_statements, security with CVE-2026-3172 and pgvector 0.8.2, multi-tenancy with RLS without over-filtering and the current_setting InitPlan, a CI recall benchmark with pytest and GitHub Actions, when pgvector stops being the answer (pgvectorscale and StreamingDiskANN, VectorChord, dedicated databases) and a full case study going from recall 0.71 and 240 ms to recall 0.96 and 26 ms. With a glossary and an expanded FAQ.
Verificando acceso...