pgvector hay vector database: nên chọn cái nào
Hướng dẫn chọn giữa pgvector và Pinecone/Qdrant/Weaviate theo chi phí, recall, vận hành và quy mô — kèm benchmark thật và một lựa chọn mặc định để bắt đầu.
Bài trụ của series này đưa ra một khẳng định rồi hẹn để dành phần chứng minh: cứ bắt đầu với pgvector, và chỉ nâng cấp lên vector database riêng khi các con số buộc bạn phải làm vậy. Bài này chính là benchmark đứng sau lời khuyên đó. Câu hỏi không phải "cái nào tốt hơn" — mà là "ở quy mô nào, với team như thế nào, thì mảnh ghép thứ hai bắt đầu xứng đáng với công vận hành."
Mọi bên bán vector database đều muốn câu trả lời là "bạn cần chúng tôi từ ngày đầu". Với đa số team backend thì không. Vậy hãy nhìn xem vector search thật sự đòi hỏi gì ở storage, một managed vector DB cho bạn cái gì mà Postgres không có, và điểm giao nằm ở đâu.
"Vector search" cần gì ở một database
Bỏ qua marketing, một vector store làm đúng ba việc:
- Lưu một mảng số thực độ dài cố định cho mỗi bản ghi (embedding của bạn), lý tưởng là nằm ngay cạnh dữ liệu mà nó mô tả.
- Đánh index các mảng đó để truy vấn nearest-neighbour không phải quét cả bảng.
- Truy vấn top-k bản ghi gần nhất với một vector truy vấn, theo một độ đo khoảng cách (cosine, L2, inner product).
Chỉ vậy thôi. Phần khó là index: tìm nearest-neighbour chính xác là O(n) mỗi truy vấn — ổn ở 10k bản ghi nhưng vô dụng ở 10M. Mọi vector store nghiêm túc — kể cả pgvector — giải bằng index xấp xỉ (ANN), đánh đổi một chút recall để lấy rất nhiều tốc độ. Hai tham số bạn thật sự quan tâm là recall (lấy đúng các hàng xóm gần nhất, hay chỉ gần-gần?) và p95 latency (truy vấn chậm thì chậm cỡ nào?). Cố định hai cái đó, phần lớn cuộc tranh luận "chọn database nào" sụp xuống còn chuyện vận hành và chi phí.
pgvector: bớt một mảnh ghép phải vận hành
Nếu bạn đã chạy Postgres — và nếu bạn theo dõi loạt bài autocomplete địa chỉ bằng full-text search trên blog này thì đúng là vậy — thêm vector search chỉ là một extension và một cột. Không service mới phải deploy, bảo mật, backup hay giữ đồng bộ.
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- Index HNSW cho tìm nearest-neighbour xấp xỉ, nhanh
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Núm chỉnh recall/tốc độ, đặt theo session hoặc transaction
SET hnsw.ef_search = 40;
-- Top-5 gần nhất, KÈM filter quan hệ trong CÙNG một truy vấn
SELECT id, content
FROM documents
WHERE tenant_id = $2 AND published
ORDER BY embedding <=> $1 -- $1 = embedding truy vấn, <=> = cosine distance
LIMIT 5;Dòng quan trọng là cái WHERE. Embedding nằm cùng hàng với tenant_id và published, nên một vector search có lọc là một truy vấn mà planner hiểu được — không phải lấy ứng viên từ một hệ rồi lọc ở hệ khác. Vector nằm trong cùng transaction với các lệnh ghi của bạn, nên không có khe dual-write và không lệch dữ liệu. Chính tính chất đó là lý do pgvector là lựa chọn mặc định đúng đắn cho phần lớn sản phẩm.
Với một store riêng, tương đương là hai hệ thống nói chuyện qua network:
// Kiểu Pinecone: vector nằm BÊN NGOÀI database của bạn
const res = await index.query({
vector: queryEmbedding,
topK: 5,
filter: { tenant_id: tenantId, published: true },
});
// ...rồi nạp đủ bản ghi từ Postgres theo id, ở vòng round-trip THỨ HAI
const rows = await db.documents.findMany({ where: { id: { in: res.matches.map((m) => m.id) } } });Hai store, hai round trip, hai thứ có thể lệch nhau. Đôi khi đáng — nhưng đổi lại bạn phải mua được một thứ cụ thể.
Vector DB riêng: bạn thật sự mua được gì
Pinecone, Qdrant, Weaviate, Milvus không phải hàng dỏm. Ở quy mô lớn chúng cho bạn những thứ mà Postgres bắt bạn tự xây:
- Shard ngang index vector qua nhiều node — lý do thật sự để rời đi, khi vượt vài chục triệu vector.
- Build index không khoá bảng. Một lần build HNSW lớn trong Postgres tranh tài nguyên với traffic production; managed store build và hoán đổi index ngoài luồng.
- Bố trí bộ nhớ chuyên dụng và quantization (ví dụ scalar/product quantization) nhồi được nhiều vector hơn trên mỗi GB RAM.
- Vận hành được lo sẵn — replication, backup, rebalancing có người làm hộ, đáng tiền thật với một team nhỏ.
Không cái nào quan trọng ở mức 100k vector. Tất cả bắt đầu quan trọng đâu đó giữa 1M và 100M, tuỳ mục tiêu latency và ngân sách của bạn. Việc cần làm là tìm con số của bạn, không phải bê con số của người khác về dùng.
Benchmark: recall & p95 ở 100k / 1M / 10M vector
Đây là dáng số liệu giữ vững qua các benchmark công khai (ANN-Benchmarks, số HNSW của chính pgvector) và các lần tôi tự chạy trên embedding OpenAI 1536 chiều. Hãy coi là mức tham khảo theo bậc độ lớn, không phải con số chính xác phòng lab — chúng dịch chuyển theo số chiều, m/ef_search, phần cứng, và cache nóng tới đâu:
| Cỡ dữ liệu | pgvector (HNSW) p95 | Vector DB riêng p95 | Recall@10 (cả hai, đã tune) | | --- | --- | --- | --- | | 100k vector | vài ms | vài ms | ~0.98+ | | 1M vector | ~10–30 ms | ~10–20 ms | ~0.95–0.98 | | 10M vector | ~50–150 ms (một máy, đã tune) | ~10–40 ms (sharded) | ~0.95 nếu RAM còn đủ |
Điểm mấu chốt: đến khoảng 1M vector, hai bên ngang nhau về recall lẫn latency. pgvector với index HNSW đã tune đạt recall y hệt một managed store ở p95 tương đương. Khoảng cách mở ra từ ~10M+, và ngay cả khi đó cũng chỉ khi một máy Postgres không còn giữ nổi index nóng trong RAM. Recall là một cái núm, không phải tính chất cố định: tăng hnsw.ef_search thì recall lên còn latency tăng theo — cả hai engine đều trả đúng cái thuế đó.
RAM tiêu tốn và thời gian build index
Ràng buộc cắn trước thật ra là RAM, không phải tốc độ truy vấn. Index HNSW muốn nằm trong bộ nhớ; một khi tràn xuống đĩa, p95 rơi thẳng đứng. Quy tắc ước lượng thô cho vector float32 1536 chiều: khoảng 6 KB cho dữ liệu thô mỗi vector cộng overhead của index, nên ~1M vector là vài GB và ~10M là vài chục GB — còn chưa tính bảng dữ liệu thật của bạn. Thời gian build index scale theo m và ef_construction, và là bất ngờ còn lại — build HNSW vài triệu hàng tốn từ vài phút tới vài giờ, mà trong Postgres còn tranh tài nguyên với traffic sống trừ khi bạn build concurrently và lúc thấp điểm. Store riêng giấu cả hai chi phí này sau quantization và build ngoài luồng; sự tiện đó là một phần thật trong cái bạn đang trả tiền.
Bảng quyết định: chọn theo quy mô, team và ngân sách vận hành
| Tình huống của bạn | Dùng pgvector | Dùng vector DB riêng | | --- | --- | --- | | < 1M vector, đã có Postgres | ✅ mặc định | hiếm khi đáng | | 1–10M vector, ngân sách p95 ≥ 50 ms | ✅ tune HNSW + RAM | chỉ khi vận hành là nút thắt | | 10M+ vector hoặc SLA latency ngặt | làm được nhưng bạn thành người vận hành vector-DB | ✅ sharding xứng công | | Lọc quan hệ nặng / multi-tenant | ✅ filter + vector trong một truy vấn | filter pushdown là điểm yếu | | Team nhỏ, không có platform engineer | ✅ một hệ phải chạy | ✅ nếu bạn muốn trả tiền thay vì vận hành | | Re-index toàn bộ corpus khổng lồ thường xuyên | build khoá traffic | ✅ rebuild ngoài luồng |
Đọc bảng theo hai trục: quy mô đẩy bạn sang phải, độ đơn giản vận hành kéo bạn về trái. Đa số team coi trọng quá mức quy mô (một con số họ đang đoán) và xem nhẹ vận hành (cái giá họ trả mỗi tuần).
Lựa chọn mặc định của tôi: bắt đầu với pgvector, nâng cấp sau
Mặt chi phí chốt hạ cho trường hợp phổ biến. Nếu đã chạy Postgres, vector chỉ thêm một cột và một index — thực tế là $0 hạ tầng mới. Một managed vector DB khởi điểm vài chục đô/tháng cho một index nhỏ rồi leo lên, nằm trên cái Postgres bạn vẫn phải chạy để giữ các bản ghi gốc. Bạn chưa trả khoản đó tới khi nó mua cho bạn một thứ đo được.
Vậy quy tắc từ bài trụ, giờ có số đứng sau:
- Bắt đầu với pgvector. Dưới ~1M vector nó ngang một store riêng về recall và latency, trong một hệ, một transaction, và lọc quan hệ miễn phí.
- Đo từ ngày đầu. Theo dõi p95 và recall theo từng truy vấn, đúng kỷ luật chi-phí-và-độ-trễ mà series này cứ quay lại — cùng tư duy observability đằng sau phân tích usage real-time bằng ClickHouse.
- Nâng cấp khi một con số buộc bạn — index không còn vừa RAM, p95 vỡ SLA ở mức
ef_searchthật của bạn, hay thời gian re-index bắt đầu làm đau production. Tới lúc đó bạn sẽ biết chính xác vì sao mình migrate, khiến cuộc migrate trở nên nhàm chán thay vì suy đoán.
Kết cục tệ nhất không phải chọn pgvector rồi vượt ngưỡng nó — đó là một vấn đề tốt với trigger rõ ràng. Kết cục tệ nhất là dựng một datastore thứ hai từ ngày đầu để giải một quy mô bạn chưa hề có. Hãy bookmark bản đồ series; các bài tiếp theo đi sâu vào embeddings, index và retrieval làm cho stack này thật sự chạy.
Hệ thống của bạn đang gặp vấn đề hiệu năng hay mở rộng tải?
Tôi chuyên xây dựng hạ tầng bản đồ độ trễ thấp, streaming pipeline thời gian thực (Kafka, ClickHouse) và các hệ thống backend tối ưu. Hãy cùng hợp tác để nâng cấp sản phẩm của bạn.
Bài viết liên quan
21 Jun 2026
AI cho Backend Engineer: Hướng dẫn thực chiến
Hướng dẫn AI cho backend engineer: embeddings, vector search, RAG và LLM API — chúng là gì, đặt ở đâu trong hệ thống, và những con số chi phí, độ trễ cần theo dõi.
20 Jun 2026
PostgreSQL Partitioning: Tối ưu bảng 100 triệu dòng với Declarative Partitioning
Hướng dẫn phân vùng bảng PostgreSQL cho bảng 100 triệu dòng: chọn RANGE/LIST/HASH, tạo declarative partition, tận dụng partition pruning để query nhanh hơn, đánh index per-partition và dọn dữ liệu gần như miễn phí bằng DETACH/DROP PARTITION.
8 Jun 2026
PostGIS Performance Tuning: Từ 2s Xuống 10ms Cho Truy Vấn Địa Lý Việt Nam (Gogoduk Case Study)
Khám phá các kỹ thuật tối ưu hóa cơ sở dữ liệu PostGIS thực tế từ dự án Gogoduk Map API. Tìm hiểu cách chuyển dịch từ Geometry sang Geography, thiết kế Partial GIST Index và rút gọn đa giác để đạt tốc độ truy vấn 10ms.
4 Jun 2026
PostgreSQL Full-Text Search: Tối Ưu Tìm Kiếm Địa Chỉ Autocomplete Tiếng Việt
Hướng dẫn chi tiết cách xây dựng hệ thống gợi ý địa chỉ tiếng Việt nhanh, không dấu và tìm kiếm lỗi chính tả bằng unaccent, FTS và Trigram Indexing trong PostgreSQL.