Bỏ qua để đến nội dung
Search & RAG

7.15 Phân tán: shard, replica, Raft

Mục này bắt đầu bằng câu quan trọng nhất, vì hiểu sai nó dẫn tới kỳ vọng sai về toàn bộ hệ:

Raft trong Qdrant quản hình dạng của cụm, không quản dữ liệu của bạn.

Collection nào tồn tại, peer nào giữ shard nào, replication factor bao nhiêu — những thứ đó đi qua đồng thuận Raft. Thao tác trên point thì không.

Đây là một quyết định thiết kế có chủ ý, và nó đánh đổi consistency lấy throughput. Nếu mọi lần upsert đều phải qua Raft, throughput ghi sẽ sập. Cái giá là mô hình consistency yếu hơn, và đó là chủ đề của 7.16.

Collection "tai_lieu"
├── Shard 0 ──► ReplicaSet ──┬── replica trên peer A ← các replica
│ └── replica trên peer B
├── Shard 1 ──► ReplicaSet ──┬── replica trên peer B
│ └── replica trên peer C
└── Shard 2 ──► ...
Mỗi replica = một tập segment đầy đủ (xem 7.4)

Shard là đơn vị phân chia dữ liệu. Replica là đơn vị dự phòng. Một truy vấn phải chạm mọi shard (mỗi shard một replica) rồi gộp kết quả — giống hệt cách một truy vấn phải chạm mọi segment trong một shard.

shard_number — con số bạn không sửa được

Phần tiêu đề “shard_number — con số bạn không sửa được”

Đây là quyết định đắt nhất khi tạo collection ở bản tự host.

  • Mặc định: số node trong cụm tại thời điểm tạo collection.
  • Không đổi được sau đó (Qdrant Cloud có resharding từ v1.13; bản tự host thì chưa).

Khuyến nghị của Qdrant: ít nhất 2 shard mỗi node, và chọn một con số có nhiều ước để sau này chia lại được. Ví dụ họ đưa ra: 12 shard cho phép chạy trên 1 → 2 → 3 → 6 → 12 node mà vẫn chia đều.

Chọn shard_number quá nhỏ ⇒ không mở rộng ngang được mà không nạp lại. Chọn quá lớn ⇒ mỗi truy vấn phải fan-out tới quá nhiều nơi, latency đuôi xấu đi. Đây là chỗ đáng dừng lại suy nghĩ 15 phút trước khi gõ lệnh tạo collection.

auto (mặc định)custom
Điểm rơi vào shard nàoconsistent hashing theo IDtheo shard_key bạn chỉ định
Bạn kiểm soát vị tríkhông
Dùng khimặc địnhmultitenancy, partition theo thời gian

Với custom, số shard vật lý = shard_number × số shard_key × replication_factor. Và có một chi tiết dễ bỏ sót: ID chỉ phải duy nhất trong phạm vi một shard key — hai shard key khác nhau có thể chứa cùng một point ID.

Công dụng thật của custom:

  • Gom một khách hàng lớn vào shard riêng. Xem 7.17.
  • Partition theo thời gian. Mỗi tháng một shard key. Xoá dữ liệu cũ = xoá nguyên một shard, thay vì xoá hàng triệu điểm rồi chờ vacuum (7.6). Đây là mẹo vận hành rất đáng giá cho dữ liệu có vòng đời.
  • Tuân thủ dữ liệu theo vùng. Dữ liệu khách hàng EU nằm trên peer đặt ở EU.

Mặc định 1 — nghĩa là không có replica nào. Một node chết là mất shard đó.

Ở bản tự host, replica không tự sinh ra: bạn tạo và gỡ chúng bằng tay, và việc đổi replication_factor trong cấu hình không tự nhân bản dữ liệu đang có. Qdrant Cloud thì tự động duy trì đúng số replica.

Chi phí là tuyến tính: replication_factor: 2 nghĩa là gấp đôi dung lượng lưu trữ.

Khi thêm node, cân bằng lại, hoặc phục hồi sau sự cố, Qdrant phải di chuyển shard. Ba cách, và chọn sai thì hoặc chậm hoặc sai:

Phương phápCó từChuyển cả index?Thứ tự ghiĐánh đổi
stream_recordsv0.8❌ dựng lại ở đíchweakChậm (phải xây lại HNSW), nhưng không cần thêm đĩa
snapshotv1.7✅ (cả quantization)strongNhanh, nhưng cần thêm dung lượng đĩa
wal_deltav1.8strongChỉ dùng được khi đích đã có shard đó — dành cho phục hồi

wal_delta là thứ Qdrant tự dùng khi một node quay lại sau khi rớt mạng ngắn: chỉ phát lại phần WAL còn thiếu thay vì chép lại toàn bộ shard. Rất rẻ, nhưng chỉ áp dụng được cho tình huống đó.

Mặc định là null — Qdrant tự chọn.

Điều gì xảy ra phụ thuộc hoàn toàn vào replication_factor:

replication_factor: 1replication_factor: 2
Shard trên node đóKhông truy cập đượcCòn replica, vẫn phục vụ
Truy vấnLỗi hoặc thiếu kết quảBình thường
GhiLỗi cho shard đóBình thường, tuỳ write_consistency_factor
Phục hồiTừ snapshot (7.19)Tự động, thường bằng wal_delta

Raft phát hiện node mất kết nối bằng ping định kỳ, chu kỳ đặt bởi tick_period_ms (mặc định 100). Giảm nó thì phát hiện nhanh hơn nhưng tốn overhead hơn.

⚠️ replication_factor: 1 không phải là “chưa cần lo về HA”. Nó là “một node chết thì một phần corpus biến mất khỏi kết quả tìm kiếm, và không có lỗi rõ ràng nào cho ứng dụng biết là kết quả đang thiếu.” Đây là chế độ hỏng âm thầm, giống họ với các chế độ hỏng ở 7.8.

Cân bằng tải là việc của bạn ở bản tự host — Qdrant không tự làm. Mọi peer đều nhận được request và sẽ chuyển tiếp tới nơi cần, nhưng peer nhận request phải gánh phần điều phối, nên dồn hết vào một node là lãng phí.

Listener node (node_type: "Listener") là chế độ thử nghiệm: nhận mọi cập nhật (xử lý như wait=false) nhưng không tham gia trả lời truy vấn. Dùng để dựng một node chuyên làm backup, hoặc đồng bộ sang vùng khác mà không ảnh hưởng đường phục vụ.

  1. shard_number là bao nhiêu, và nó chia được cho những số node nào? Sai là nạp lại.
  2. replication_factor đã > 1 chưa? Nếu chưa, bạn đang chấp nhận mất dữ liệu âm thầm.
  3. Tôi có cần custom sharding không? Multitenancy và dữ liệu theo thời gian là hai dấu hiệu rõ ràng nhất.
  4. Fusion của tôi có nằm trong prefetch không? Nếu có, nó đang tính theo từng shard — xem 7.12. Đây là bug chỉ xuất hiện khi lên nhiều shard.
Phần 7 — Qdrant