7.2 Collection, point, vector, payload
Bốn khái niệm. Học xong bốn cái này là bạn đọc được mọi request của Qdrant.
Collection "tai_lieu_ho_tro" ← đơn vị cấu hình: số chiều, khoảng cách, shard └── Point id = 42 ← đơn vị dữ liệu ├── vector(s) [0.12, -0.03, …] ← thứ dùng để đo "giống nhau" └── payload {"phong_ban": "ktoan", "cap_nhat": "2026-08-01", "text": "…"} ← thứ dùng để lọc, và để trả về cho ứng dụngMột câu để nhớ: vector quyết định thứ tự, payload quyết định ai được vào cuộc chơi.
Collection — và bốn quyết định không sửa được về sau
Phần tiêu đề “Collection — và bốn quyết định không sửa được về sau”Collection là đơn vị bạn tạo, cấu hình, sharding và backup. Vấn đề: một vài lựa chọn khi tạo collection đóng đinh vĩnh viễn hoặc cực đắt để đổi. Liệt kê ra đây vì đây là chỗ người mới trả giá nhiều nhất:
| Quyết định | Đổi được sau không? | Hậu quả nếu chọn sai |
|---|---|---|
| Số chiều của mỗi named vector | Không | Đổi model embedding sang số chiều khác ⇒ tạo collection mới, nạp lại toàn bộ |
Khoảng cách (Cosine, Dot, Euclid, Manhattan) | Không | Điểm số vô nghĩa, hoặc lệch hẳn thứ hạng |
shard_number | Không (bản tự host); Qdrant Cloud có resharding từ v1.13 | Muốn thêm node mà không chia nhỏ được ⇒ nạp lại |
sharding_method (auto / custom) | Không | Mất khả năng gom một tenant vào một shard |
Ngược lại, những thứ đổi được lúc chạy: hnsw_config, optimizers_config,
quantization_config, memory tier, replication_factor, strict_mode_config, và việc
thêm/xoá named vector (từ v1.18). Nghĩa là: đừng tối ưu hiệu năng lúc tạo collection.
Hãy chọn đúng bốn thứ ở bảng trên, rồi tinh chỉnh phần còn lại sau khi đã đo.
Chọn khoảng cách nào. Nếu embedding model của bạn được huấn luyện với cosine similarity (đa số model câu/đoạn hiện nay), dùng
Cosine. Nếu vector đã được chuẩn hoá về độ dài 1 thìDotcho cùng thứ hạng mà rẻ hơn một phép chia.EuclidvàManhattanchỉ dùng khi bạn biết chắc mình cần — và lưu ýManhattan(L1) làm hỏng phần lớn lựa chọn quantization (7.10).
Alias — cách thay index mà không rớt dịch vụ
Phần tiêu đề “Alias — cách thay index mà không rớt dịch vụ”Collection có thể có alias: một tên khác trỏ tới nó, và việc đổi alias là nguyên tử. Đây là công cụ triển khai quan trọng hơn vẻ ngoài của nó:
ứng dụng luôn hỏi: "tai_lieu" (alias) │ hôm nay ───────┴──► tai_lieu_v3 (model embedding cũ) sau khi nạp xong ──► tai_lieu_v4 (model mới, số chiều khác)Vì số chiều không đổi được, đổi model embedding luôn có nghĩa là tạo collection mới. Alias là thứ biến việc đó thành một thao tác đổi tên thay vì một lần downtime. Ghi chú: alias không nằm trong snapshot cấp collection (7.19).
Trạng thái collection
Phần tiêu đề “Trạng thái collection”Bốn màu, và mỗi màu có nghĩa vận hành rõ ràng:
| Màu | Nghĩa | Bạn nên làm gì |
|---|---|---|
| 🟢 green | Sẵn sàng | Không gì cả |
| 🟡 yellow | Đang tối ưu (optimizer chạy) | Bình thường sau khi nạp dữ liệu. Nhưng nếu vàng mãi thì xem 7.6 |
| ⚫ grey | Tối ưu đang chờ, cần một thao tác cập nhật để tiếp tục | Gửi một update bất kỳ |
| 🔴 red | Lỗi không phục hồi được | Đọc log; nghĩ tới snapshot |
Point — và cái bẫy ID
Phần tiêu đề “Point — và cái bẫy ID”Point là một bản ghi: một ID, một hoặc nhiều vector, và một payload.
ID chỉ nhận hai kiểu: số nguyên không dấu 64 bit, hoặc UUID. Không có chuỗi tự do.
Đây là chỗ vấp đầu tiên của gần như mọi người: "doc-123#chunk-4" không phải một ID
hợp lệ.
Cách xử lý đúng, và cũng là cách nên làm ngay từ đầu:
Băm khoá nghiệp vụ của bạn thành UUID, rồi cất khoá gốc vào payload. ID để Qdrant dùng; payload để bạn đọc. Dùng UUIDv5 (băm xác định từ một namespace) thì cùng một chunk luôn ra cùng một ID — nghĩa là nạp lại tài liệu sẽ ghi đè đúng điểm cũ thay vì sinh trùng lặp.
Điều này quan trọng hơn nó có vẻ: upsert trong Qdrant thay thế toàn bộ point. Nếu
ID không xác định được từ nội dung, mỗi lần bạn nạp lại tài liệu là một lần corpus phình
lên với các bản sao ma — và recall@k của bạn tụt vì top-k bị chiếm bởi các bản sao.
Xoá, và vì sao dữ liệu đã xoá vẫn còn tốn chỗ
Phần tiêu đề “Xoá, và vì sao dữ liệu đã xoá vẫn còn tốn chỗ”Xoá trong Qdrant là xoá mềm: điểm bị đánh dấu, còn chỗ trống chỉ được thu hồi khi
vacuum optimizer chạy — theo mặc định là khi tỷ lệ điểm đã xoá trong một segment vượt
deleted_threshold: 0.2 và segment có ít nhất vacuum_min_vector_number: 1000 vector
(config.yaml v1.19.0).
Hệ quả thực tế: sau một đợt xoá lớn, dung lượng không giảm ngay, và các điểm đã xoá vẫn nằm trong đồ thị HNSW làm chậm việc đi đồ thị cho tới khi segment được xây lại. Chi tiết ở 7.6.
Payload — JSON tuỳ ý, nhưng đừng đối xử với nó như JSON tuỳ ý
Phần tiêu đề “Payload — JSON tuỳ ý, nhưng đừng đối xử với nó như JSON tuỳ ý”Payload là một object JSON bất kỳ gắn vào point. Không cần khai báo schema. Điều đó tiện, và cũng là cái bẫy: payload không được index thì filter trên nó vẫn chạy, chỉ là chạy chậm và làm hỏng cơ chế filterable HNSW (7.9).
Ba nhóm trường trong payload, nên tách rõ trong đầu:
| Nhóm | Ví dụ | Có cần index không? | Có nên để RAM không? |
|---|---|---|---|
| Trường lọc | phong_ban, tenant_id, cap_nhat, ngon_ngu | Bắt buộc | Có — Qdrant giữ trường đã index trong RAM bất kể payload storage nằm đâu |
| Trường trả về | text, tieu_de, url | Không | Không — đây là phần nặng, để trên đĩa |
| Trường vô dụng | toàn bộ JSON gốc “cho chắc” | Không | Xoá nó đi |
Nhóm thứ ba là nguồn tốn RAM lặng lẽ hay gặp nhất. Mặc định của Qdrant là
on_disk_payload: true (nay được thay bằng payload.memory: cold), nghĩa là payload đọc
từ đĩa mỗi lần cần — đúng với trường trả về, và không ảnh hưởng tới trường đã index vì
những trường đó vẫn nằm trong RAM.
Truy cập trường lồng nhau
Phần tiêu đề “Truy cập trường lồng nhau”Qdrant hiểu đường dẫn kiểu chấm và phép chiếu mảng: "quoc_gia.ten",
"thanh_pho[].dan_so". Nhưng lọc trên mảng object có một cái bẫy nghiêm trọng đủ để
dành riêng một mục — xem 7.13, phần nested.
Ba câu hỏi để tự kiểm mô hình dữ liệu
Phần tiêu đề “Ba câu hỏi để tự kiểm mô hình dữ liệu”Trước khi nạp một triệu điểm, trả lời ba câu này. Trả lời sai thì phải nạp lại:
- Số chiều và khoảng cách có khớp với model embedding không? Kiểm bằng cách tính cosine của một cặp câu bạn biết là giống nhau, so với điểm Qdrant trả về.
- ID có sinh ra được từ nội dung không? Nếu không, bạn không có cách nào nạp lại mà không sinh bản sao.
- Những trường nào sẽ xuất hiện trong
filter? Đó chính xác là danh sách payload index bạn phải tạo trước khi nạp dữ liệu (7.9).