Sentinel cho khả năng chịu lỗi nhưng mọi dữ liệu vẫn nằm trên một máy. Cluster chia dữ liệu ra nhiều máy — và lấy đi vài thứ. Bài này dựng một cụm sáu nút rồi đo cả hai mặt.

Cách chia slot, chi phí chuyển hướng, và giới hạn lệnh nhiều khoá

16.384 slot chia cho ba nút chính

    0 – 5460    nút 1
 5461 – 10922   nút 2
10923 – 16383   nút 3

slot = CRC16(khoá) mod 16384

Không có bảng tra, không có nút điều phối. Mỗi khách hàng tự tính slot từ tên khoá và biết ngay phải hỏi nút nào.

Phân bố thực tế với 20.000 khoá: 6.669 / 6.668 / phần còn lại — đều nhau. CRC16 rải tốt, không cần làm gì thêm.

Khách hàng biết bản đồ hay không: chênh 1,63 lần

Tôi viết hai khách hàng làm cùng một việc.

Khách "ngây thơ" luôn gửi tới nút đầu tiên và chấp nhận bị chuyển hướng:

10.390 ops/s | 13.337 lần MOVED trên 20.000 lệnh (67%)

Khách tự tính CRC16 và gọi thẳng đúng nút:

16.907 ops/s | 0 lần MOVED

Chậm hơn 1,63 lần, và con số 67% chính là tỷ lệ khoá không thuộc nút đầu — 2 trên 3 phân mảnh.

Mỗi MOVED là một lượt đi về bị bỏ phí: gửi lệnh, nhận lỗi kèm địa chỉ đúng, gửi lại. Với cụm 10 nút, tỷ lệ đó là 90%.

Đây là lý do thư viện khách hàng cho Cluster phải nạp và giữ bản đồ slot. Nếu bạn dùng một thư viện không hỗ trợ Cluster và chỉ dựa vào MOVED, bạn đang trả giá này ở mọi lệnh.

Bản đồ cũng phải làm mới khi cụm thay đổi: sau một lần chuyển đổi hoặc một lần chuyển slot, bản đồ cũ sai và khách quay lại chế độ chuyển hướng. Thư viện tốt nghe MOVED rồi tải lại CLUSTER SLOTS.

Cái Cluster lấy đi

MGET k:1 k:2 k:3   ->  CROSSSLOT Keys in request don't hash to the same slot
MSET a 1 b 2       ->  CROSSSLOT
EVAL ... 2 k:1 k:2 ->  CROSSSLOT

k:1 ở slot 10166, k:2 ở slot 6101 — hai nút khác nhau, và Redis từ chối thẳng.

Điều này áp cho mọi lệnh chạm nhiều khoá: MGET, MSET, SINTER, ZUNIONSTORE, MULTI, và Lua. Tất cả chỉ làm việc trong một slot.

Hệ quả với những gì đã đo ở các phần trước:

  • MGET — thứ nhanh hơn đường ống 45% ở phần 12 — không dùng được qua slot.
  • Đường ống vẫn dùng được, nhưng phải chia theo nút.
  • Lua và MULTI mất phần lớn ích lợi: chúng chỉ nguyên tử được trong một slot.

Đây là cái giá thật của Cluster, và nó không phải hiệu năng mà là khả năng diễn đạt.

Thẻ băm là lối thoát duy nhất

{u1}:ten   ->  slot 4574
{u1}:tuoi  ->  slot 4574

MSET {u1}:ten A {u1}:tuoi 30   ->  OK
MGET {u1}:ten {u1}:tuoi        ->  A 30

Chỉ phần trong {} được băm. Mọi khoá cùng thẻ nằm cùng slot, và mọi lệnh nhiều khoá hoạt động lại bình thường.

Nhưng nó phải được thiết kế từ lúc đặt tên khoá. Đổi lược đồ tên khoá trên hệ thống đang chạy là một dự án di trú, không phải một dòng cấu hình.

Và nó có mặt trái: thẻ băm gom dữ liệu vào một slot, nên một thẻ "nóng" tạo ra một nút nóng. Thẻ nên gom đúng những gì cần truy cập cùng nhau — một người dùng, một phiên — không nên gom cả một danh mục.

Khi nào cần Cluster

Từ những gì đo được, ba lý do đứng vững:

  • Dữ liệu không vừa một máy. Đây là lý do chính đáng nhất và cũng dễ nhận ra nhất.
  • Thông lượng ghi vượt một luồng. Phần 2 đo được trần một nhân; Cluster là cách duy nhất vượt qua nó với Redis nguyên bản.
  • Cần chịu lỗi tự động mà không muốn chạy Sentinel riêng. Cluster có sẵn phát hiện lỗi và chuyển đổi.

Và lý do không đứng vững: "để mở rộng sau này". Cluster lấy đi lệnh nhiều khoá, Lua qua slot, và giao dịch — ba thứ mà mã của bạn có thể đang dựa vào. Chuyển sang Cluster sau khi đã viết mã dựa vào chúng là viết lại, không phải cấu hình.

Ranh giới thực dụng: một Redis có bản sao phục vụ được rất nhiều. Với 145 GB RAM và 300.000 ops/s trên một nút, phần lớn hệ thống không bao giờ chạm tới ranh giới đó. Đo trước khi chia.

Ba điều nên biết khi vận hành

Sáu nút là tối thiểu thực tế. Ba chính và ba bản sao. Cụm chỉ có nút chính thì mất một nút là mất luôn phần dữ liệu đó, và cụm chuyển sang trạng thái lỗi.

cluster-require-full-coverage mặc định yes: mất một phân mảnh là cả cụm ngừng phục vụ. Đặt no cho phép phần còn lại tiếp tục — hợp với bộ đệm, không hợp với dữ liệu cần đầy đủ.

Chuyển slot là thao tác trực tuyến nhưng không rẻ. redis-cli --cluster reshard di chuyển khoá theo từng lô, và trong lúc đó khách nhận ASK — một dạng chuyển hướng tạm thời. Thư viện phải hiểu cả MOVED lẫn ASK; nhiều thư viện cũ chỉ hiểu cái đầu.

Thử ba mươi giây

Kiểm cụm của bạn có cân bằng không và khách có đang bị chuyển hướng không:

redis-cli --cluster check <mot-nut>:6379
redis-cli -c cluster info | grep -E 'cluster_state|cluster_slots_ok|cluster_known_nodes'

# so so khoa tren tung nut chinh
for n in <ip1> <ip2> <ip3>; do
  echo "$n: $(redis-cli -h $n dbsize)"
done

Số khoá lệch nhau nhiều giữa các nút thường có một nguyên nhân: thẻ băm dùng quá rộng. Một thẻ gom hàng triệu khoá vào một slot làm cả slot đó nằm trên một nút, và không có cách nào chia nhỏ nó ngoài việc đổi tên khoá.

Phần sau: khoá phân tán — đo Redlock và những chỗ nó không bảo đảm điều bạn tưởng.