Đến 2026, vector search đã thành một kỹ năng DB cơ bản ngang với indexing truyền thống. Bất cứ hệ thống nào có RAG (Retrieval Augmented Generation), semantic search, gợi ý sản phẩm theo nội dung, dedup, hay anomaly detection đều đụng đến nó. Bài này đi từ concept tới tuning thực chiến, tập trung vào pgvector (vì bạn đã có PostgreSQL) và đối chiếu với các vector DB chuyên dụng.
1. Embedding và similarity — tại sao cần index mới?
Text/ảnh/audio/code được chuyển thành vector embedding (ví dụ 768–3072 chiều) bằng model như text-embedding-3-small, all-MiniLM-L6, bge-m3. Tìm item “tương tự nhất” với query = tìm vector gần nhất theo khoảng cách:
- Cosine distance — hay dùng nhất cho text
- L2 (Euclidean) — hay dùng cho ảnh, audio
- Inner product — khi embedding đã chuẩn hoá
Vấn đề: kNN chính xác = quét toàn bộ N vector × D chiều = O(N·D). Với 10M documents × 1536 dim = 60 GB tính toán mỗi query → không khả thi. Giải pháp: Approximate Nearest Neighbor (ANN) — đánh đổi một ít độ chính xác lấy tốc độ 100-1000×.
2. pgvector — vector search ngay trong PostgreSQL
pgvector là extension PG tốt nhất 2026 cho đa số use case: đã tích hợp vào RDS/Cloud SQL/Supabase, hỗ trợ 2 loại index ANN (HNSW + IVFFlat), có cả Bedrock/OpenAI/Voyage client Python/Node sẵn.
2.1 Cài đặt và schema cơ bản
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
body TEXT NOT NULL,
tenant_id INT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
embedding vector(1536) -- kích thước phải khớp model
);
-- Chèn dữ liệu
INSERT INTO documents (title, body, tenant_id, embedding)
VALUES ('Postgres tuning', '...', 1,
'[-0.0124, 0.9821, ..., 0.0003]'::vector);
2.2 Query kNN chưa có index
-- 3 toán tử khoảng cách:
-- <-> L2 distance
-- <#> negative inner product
-- <=> cosine distance
SELECT id, title, 1 - (embedding <=> $1) AS similarity
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;
Không có index, PostgreSQL sequential scan toàn bảng — OK cho < 100k row, nhưng từ 1M trở lên cần ANN index.
2.3 HNSW vs IVFFlat — chọn cái nào?
| Tiêu chí | HNSW (mặc định hiện nay) | IVFFlat |
|---|---|---|
| Build time | Chậm (5-10× IVFFlat) | Nhanh |
| Query latency | Thấp, ổn định | Thấp hơn khi tune đúng, nhưng sensitive |
| Recall cao | Tốt (99%+) | Cần nhiều probe, tune phức tạp |
| Update / insert | On-the-fly, ổn | Phải REINDEX khi phân phối đổi nhiều |
| RAM | Cao (cả graph trong RAM) | Thấp hơn |
| Đề xuất 2026 | Default cho RAG / search | Chỉ khi RAM khan, data tĩnh |
HNSW index:
CREATE INDEX idx_documents_embedding_hnsw
ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Runtime tuning — tăng ef_search để cao recall (đổi lấy latency)
SET hnsw.ef_search = 100; -- mặc định 40
IVFFlat index:
-- Số cluster ~= sqrt(N rows); với 1M row dùng lists = 1000
CREATE INDEX idx_documents_embedding_ivf
ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1000);
-- Runtime: tăng probes để tăng recall
SET ivfflat.probes = 10; -- mặc định 1 (recall thấp)
Heuristic: dùng HNSW. Chỉ động đến IVFFlat khi dataset > 50M vector và RAM là vấn đề.
2.4 Halfvec, binary quant và DiskANN (pgvector 0.7+, 2024-2026)
pgvector từ 0.7 hỗ trợ lưu vector ở precision thấp hơn — cắt 50-75% dung lượng và tăng tốc:
-- halfvec: float16 (1/2 kích thước, recall gần như không đổi)
ALTER TABLE documents
ADD COLUMN embedding_half halfvec(1536);
CREATE INDEX ON documents USING hnsw (embedding_half halfvec_cosine_ops);
-- bit: binary quantization (1/32 kích thước) — rerank sau bằng full vector
ALTER TABLE documents
ADD COLUMN embedding_bit bit(1536);
-- Pipeline phổ biến: binary prefilter → fetch → rerank bằng halfvec/float32
WITH candidates AS (
SELECT id, embedding
FROM documents
ORDER BY embedding_bit <~> $1_bit
LIMIT 200
)
SELECT id, title, 1 - (embedding <=> $1) AS sim
FROM candidates
ORDER BY embedding <=> $1
LIMIT 10;
DiskANN (pgvector-bgworker / pgvectorscale của Timescale) là index thế hệ 2024+ — vector lưu trên disk SSD, chỉ quantized vector + graph trong RAM. Cho phép 100M+ vector trên 1 node PG với RAM nhỏ gấp 10 lần HNSW.
3. Hybrid search — vector + BM25 + metadata
Production-grade RAG hầu như không dùng mỗi vector — kết hợp thêm keyword (BM25/FTS) + metadata filter:
-- Hybrid: tenant filter + vector similarity + text keyword
WITH vec_top AS (
SELECT id, 1 - (embedding <=> $query_vec) AS vec_score
FROM documents
WHERE tenant_id = $tenant
AND created_at >= now() - INTERVAL '1 year'
ORDER BY embedding <=> $query_vec
LIMIT 100
),
kw_top AS (
SELECT id,
ts_rank_cd(
to_tsvector('simple', title || ' ' || body),
plainto_tsquery('simple', $query_text)
) AS kw_score
FROM documents
WHERE tenant_id = $tenant
AND to_tsvector('simple', title || ' ' || body)
@@ plainto_tsquery('simple', $query_text)
LIMIT 100
)
SELECT d.id, d.title,
COALESCE(v.vec_score, 0) * 0.7
+ COALESCE(k.kw_score, 0) * 0.3 AS score
FROM documents d
LEFT JOIN vec_top v ON v.id = d.id
LEFT JOIN kw_top k ON k.id = d.id
WHERE v.id IS NOT NULL OR k.id IS NOT NULL
ORDER BY score DESC
LIMIT 10;
Reciprocal Rank Fusion (RRF) là công thức fusion chuẩn hơn điểm số tuyến tính vì chuẩn hoá thang điểm khác nhau. Công thức:
score = sum(1 / (k + rank_i))với k ≈ 60.
Filtered vector search — lưu ý pre-filter vs post-filter
-- post-filter (mặc định) — ANN trước, filter sau. Dễ miss nếu filter loose
SELECT * FROM documents
WHERE tenant_id = 1
ORDER BY embedding <=> $1 LIMIT 10;
-- Pre-filter với partial index — fast cho multi-tenant
CREATE INDEX idx_docs_tenant1_hnsw ON documents
USING hnsw (embedding vector_cosine_ops)
WHERE tenant_id = 1;
-- Hoặc partition theo tenant_id → index riêng mỗi partition
Quy tắc vàng: nếu filter loại > 99% dữ liệu thì post-filter sẽ miss; dùng partial index, partitioning, hoặc vector DB chuyên dụng hỗ trợ filtered HNSW (Qdrant, Weaviate).
4. Vector DB chuyên dụng — khi nào?
| DB | Điểm mạnh | Khi nào chọn |
|---|---|---|
| pgvector | Tích hợp PG, ACID, hybrid search dễ | 90% use case vừa và nhỏ, < 50M vector |
| Qdrant | Filtered HNSW nhanh, Rust, self-host dễ | Cần filter phức tạp + high throughput |
| Milvus / Zilliz | Scale tỷ vector, nhiều index, distributed | 500M+ vector, dedicated workload |
| Weaviate | Built-in vectorization, GraphQL, module | Full stack RAG ít tự viết pipeline |
| Pinecone | Managed, serverless | Không muốn tự vận hành |
| Vespa | Ranking phức tạp, BM25 + tensor | Kết hợp search engine |
| Elastic / OpenSearch | Đã chạy ES, cần vector phụ | Tận dụng hạ tầng cũ |
Nếu đã có PostgreSQL và workload < 50M vector: pgvector + halfvec + HNSW là đủ. Đừng tạo thêm data store khi không cần thiết.
5. Tuning & monitoring thực chiến
5.1 Các tham số cần đo
- Recall @ K (vs ground truth k-NN chính xác): phải > 0.95 mới chấp nhận cho RAG
- p50 / p95 / p99 latency query vector
- QPS max trước khi latency tăng vọt
- Index build time, disk footprint
- RAM usage (cần > dung lượng graph HNSW)
5.2 Benchmark bằng ann-benchmarks
# ann-benchmarks: so sánh index, distance, recall-latency trade-off
docker run --rm ann-benchmarks \
python run.py --dataset glove-100-angular --algorithm pgvector-hnsw
5.3 Lỗi hay gặp khi đưa lên production
- Embedding version drift: đổi model → toàn bộ embedding cũ không match. Luôn lưu
model_versioncạnh vector; có pipeline re-embed khi upgrade. - Normalization: inner product chỉ cho kết quả đúng khi vector đã normalize L2 = 1. Forget → recall tụt.
- RAM lập chỉ mục (maintenance_work_mem): PG cần đủ RAM cho build HNSW — set
SET maintenance_work_mem = '8GB';khi CREATE INDEX. - VACUUM gây rebuild HNSW từng phần — plan maintenance window.
- Chunking kém: embed cả bài dài 10k token thành 1 vector → embedding mờ. Chia thành chunks 200-500 token, overlap 50 token.
- Không rerank: top-10 từ ANN thường có noise; thêm cross-encoder rerank (bge-reranker-v2) cho precision.
5.4 Pipeline RAG production tiêu biểu
flowchart LR
Q[User query] --> E[Embedding API]
E --> V[pgvector HNSW<br/>top-K=50]
V --> F[Metadata/ACL filter]
F --> R[Cross-encoder rerank<br/>top-K=5]
R --> L[LLM + context]
L --> A[Answer + citations]
6. Checklist triển khai
- Chọn model embedding, cố định version, log vào bảng
-
vector(N)/halfvec(N)khớp N dim - HNSW index với
m=16, ef_construction=64, tuneef_searchruntime - Hybrid: BM25/FTS + vector, fusion bằng RRF
- Pre-filter qua partial index hoặc partitioning nếu multi-tenant
- Benchmark recall@10 ≥ 0.95 trước go-live
- Monitor p99 latency, cache rate, RAM usage, index bloat
- Cross-encoder rerank top-10 nếu dùng RAG
- Re-embed pipeline khi đổi model
Kết luận
Vector database không còn là lĩnh vực riêng của ML team — nó là một loại index mới, giống như B-tree hay GIN, và cần được quản trị với đúng kỷ luật: đo recall, tune như tune index SQL, monitor như bất kỳ workload production nào. Với pgvector, bạn có thể bắt đầu ngay trên PostgreSQL cluster hiện có, và chỉ chuyển sang vector DB chuyên dụng khi workload vượt ngưỡng — không phải “theo trend” AI.
Bài tiếp theo sẽ đi vào CDC (Change Data Capture) và outbox pattern — cách đồng bộ dữ liệu giữa OLTP DB với data lake, search engine, và cả vector DB ở trên một cách đáng tin cậy.