7.10 Mô hình bộ nhớ: pinned / cached / cold
Mục này chữa một hiểu lầm cụ thể, hay gặp, và tốn tiền:
“Tôi bật quantization để tiết kiệm RAM, mà RAM lại tăng.”
Không phải lỗi. Đó là hành vi đúng, và mục này giải thích vì sao.
Ba tầng bộ nhớ
Phần tiêu đề “Ba tầng bộ nhớ”Từ v1.19, Qdrant thống nhất mọi lựa chọn bộ nhớ về một khoá duy nhất: memory, với ba
giá trị. Nó thay cho các cờ on_disk rải rác trước đây (các cờ cũ vẫn chạy nhưng đã bị
đánh dấu deprecated).
| Tầng | Nghĩa | Dùng cho |
|---|---|---|
pinned | Bắt buộc nằm trong RAM | Payload index, bản vector đã nén |
cached | Nạp sẵn vào page cache lúc khởi động | Vector gốc trên hệ cần nhanh ngay từ request đầu |
cold | Không nạp sẵn; đọc từ đĩa, hệ điều hành tự cache dần | Vector gốc trên tập lớn, payload trả về |
Điểm dễ bỏ sót nhất: cached và cold đều là mmap. Cả hai đều là “file trên đĩa được
ánh xạ vào bộ nhớ” (7.5). Khác biệt duy nhất là Qdrant có
chủ động nạp trước hay không.
Hệ quả thực tế:
coldtrên máy nhiều RAM rảnh ≈ nhanh bằngcachedsau vài phút chạy, vì page cache của hệ điều hành đã giữ hết. Chỉ request đầu tiên chậm.cachedtrên máy thiếu RAM không cứu được gì — hệ điều hành vẫn sẽ đẩy trang ra.
Nghĩa là memory không phải công tắc “RAM hay đĩa”. Nó là gợi ý về thứ tự ưu tiên, và
thứ quyết định thật sự vẫn là: tập dữ liệu nóng của bạn có vừa RAM hay không.
Điều này cũng giải thích vì sao đo Qdrant bằng RSS của tiến trình hầu như luôn sai. Bộ nhớ thật đang được dùng nằm ở page cache của hệ điều hành, không nằm trong RSS.
Hai thứ khác nhau: datatype và quantization_config
Phần tiêu đề “Hai thứ khác nhau: datatype và quantization_config”Đây là gốc rễ của hiểu lầm ở đầu mục.
datatype | quantization_config | |
|---|---|---|
| Nó là gì | Cách lưu vector gốc | Một bản nén thêm vào bên cạnh vector gốc |
| Giá trị | float32, float16, uint8, turbo4 | scalar / product / binary / TurboQuant |
| Vector gốc còn không? | Không — nó là vector gốc | Còn (trừ khi bạn đẩy nó sang cold) |
| Tổng dung lượng | giảm | tăng, trừ khi bạn xử lý vector gốc |
datatype: float16 [vector 16-bit] ← chỉ một bản. Dung lượng giảm một nửa.
quantization_config: scalar [vector float32 gốc] + [bản int8 nén] ← HAI bản. Tổng dung lượng TĂNG 25%. │ │ └─ dùng để rescore └─ dùng để tìm nhanhBật quantization mà vẫn giữ vector gốc pinned/cached thì bộ nhớ tăng. Cái bạn được
là tốc độ (so sánh int8 rẻ hơn float32 và được SIMD tăng tốc), không phải bộ nhớ.
Muốn tiết kiệm bộ nhớ thật thì phải làm một trong hai:
Cách A: vector gốc → cold + bản nén → pinned ⇒ RAM chỉ giữ bản nén. Đây là cấu hình mà mọi hướng dẫn "tiết kiệm RAM" thật sự nói tới.
Cách B: datatype: float16 (hoặc turbo4) và KHÔNG bật quantization ⇒ chỉ một bản, nhỏ hơn. Đơn giản nhất, không cần rescore/oversampling.Cách B nên thử trước. Nó rẻ, dễ hoàn tác, và không kéo theo hai núm vặn mới.
Chọn mức nén nào
Phần tiêu đề “Chọn mức nén nào”6.1 đã giải thích cơ chế scalar / PQ / binary / TurboQuant và Tầng 5 đã có bảng chọn mức nén. Không lặp lại ở đây. Ba điều riêng của Qdrant cần nhớ:
rescore— lấy top-k thô bằng bản nén rồi chấm lại bằng vector gốc. Đây là thứ mua lại phần lớn recall đã mất. Nhưng nó đòi vector gốc còn truy cập được: nếu vector gốc nằmcoldtrên đĩa chậm, rescore giết latency. Khi đó Qdrant khuyên tắt rescore và tăngoversampling.oversampling— lấyoversampling × limitứng viên trước khi rescore. Đây là núm đổi latency lấy chất lượng tại runtime, giống vai tròhnsw_efcủa HNSW.- Đo trước khi tin. Chạy lại đúng bộ truy vấn với
params: {quantization: {ignore: true}}rồi so hai con số. Đó là lượng recall quantization đang lấy đi của bạn.
Đừng bật quantization mà bỏ bước 3. Nguyên tắc này giống hệt
exact: trueở 7.7: bạn cần một baseline, nếu không mọi tối ưu chỉ là niềm tin.
Inline storage — chữa bệnh của HNSW trên đĩa
Phần tiêu đề “Inline storage — chữa bệnh của HNSW trên đĩa”Từ v1.16. Đây là tính năng đáng chú ý nhất cho ai chạy tập dữ liệu lớn hơn RAM.
Vấn đề: đi đồ thị HNSW là nhảy ngẫu nhiên. Mỗi bước cần hai thứ ở hai chỗ khác nhau trên đĩa — danh sách láng giềng của node, và vector để chấm điểm. Với đĩa, hai lần đọc rải rác đắt hơn nhiều so với một lần đọc liền mạch.
Inline storage nhúng vector đã nén thẳng vào node HNSW, cạnh danh sách láng giềng. Một lần đọc trang 4 KB lấy được cả hai.
Số Qdrant công bố (số dẫn từ nguồn ngoài — 1 triệu vector, giới hạn 430 MB RAM):
| QPS | Độ chính xác | |
|---|---|---|
| Không inline storage | 20 | — |
| Có inline storage | 211 | 86,92% (nhờ rescore ngầm) |
Hơn 10× trên cùng phần cứng. Cái giá: dung lượng đĩa tăng khoảng 3–4×, và nó đòi quantization phải bật. Đây là đánh đổi rất đáng khi RAM đắt hơn đĩa — tức là hầu hết mọi lúc trên cloud.
Ba cấu hình mẫu
Phần tiêu đề “Ba cấu hình mẫu”Ba điểm trên đường cong, lấy từ hướng dẫn tối ưu của Qdrant. Dùng chúng làm điểm xuất phát, đừng dùng làm kết luận:
A. Nhanh + ít RAM — vector gốc cold, bản nén pinned, cân nhắc tắt rescore.
Đây là cấu hình cho tập lớn, ngân sách RAM chặt.
B. Chính xác + ít RAM — cả vector lẫn HNSW đều cold, bù lại bằng m và ef_construct
cao (Qdrant gợi ý tới m: 64, ef_construct: 512), và bật inline storage. Hiệu năng ở đây
phụ thuộc IOPS của đĩa — hãy đo bằng fio trước khi chọn, đừng đoán.
C. Chính xác + nhanh — mọi thứ trong RAM, thêm quantization để tăng tốc so sánh. Đây là cấu hình đắt nhất và cũng đơn giản nhất.
Thứ tự thử khi RAM chật
Phần tiêu đề “Thứ tự thử khi RAM chật”Từ rẻ nhất tới đắt nhất, và dừng lại ngay khi đủ:
| # | Việc | Được gì | Mất gì |
|---|---|---|---|
| 1 | Dọn payload thừa, để payload cold | Thường rất nhiều | Không gì |
| 2 | datatype: float16 | 2× | Gần như không |
| 3 | Giảm số chiều embedding (Matryoshka) | tuỳ | Cần đo lại chất lượng |
| 4 | Giảm m | m × 8 byte/vector | Recall |
| 5 | Vector gốc → cold, bản nén → pinned | Rất nhiều | Latency nếu đĩa chậm |
| 6 | Bật inline storage | Cứu latency của bước 5 | Đĩa 3–4× |
| 7 | Nén sâu hơn (TurboQuant / binary / PQ) | Rất nhiều | Recall — phải đo |
Bước 1 bị bỏ qua nhiều nhất và thường cho nhiều nhất. Rất nhiều collection đang giữ nguyên cả JSON gốc của tài liệu trong payload “cho chắc” (7.2).