Một toà nhà có máy phát điện dự phòng với bộ chuyển nguồn tự động. Khi điện lưới mất, máy phát nhận việc — nhưng luôn có một khoảng tối: cảm biến phải chắc chắn lưới đã mất chứ không phải chớp nháy, máy phát khởi động, bộ chuyển gạt sang. Bảng thông số ghi "chuyển trong 4,5 giây". Nhưng người ngồi trong phòng cảm nhận khoảng tối dài hơn thế — vì cái máy tính của họ còn phải khởi động lại, cái điều hoà còn phải nối lại. Thời gian bộ chuyển nguồn báo và thời gian người dùng cảm nhận là hai con số khác nhau. Sentinel của Redis đúng là bộ chuyển nguồn tự động ấy, và bài này đo cả hai con số đó.
Phần trước đo được sao chép giữ dữ liệu tốt cho tới khi máy chính chết — và rồi không có ai thay nó. Sentinel là thứ làm việc thay đó. Ta đo nó mất bao lâu.
Dòng thời gian một lần chuyển đổi
Ba Sentinel, quorum 2, một máy chính và hai bản sao.
down-after-milliseconds 3000
t = 0,00 s SIGKILL máy chính
t = 3,30 s một Sentinel đánh dấu s_down
t = 4,50 s máy chính mới được công bố
Đổi down-after-milliseconds xuống 1000:
t = 2,48 s máy chính mới được công bố
Quan hệ khá rõ: thời gian chuyển đổi ≈ down-after + khoảng 1,5 giây cho bầu chọn và thăng cấp. Phần 1,5 giây gần như cố định — Sentinel phải đạt o_down (đủ quorum đồng ý), bầu một Sentinel dẫn dắt, chọn bản sao tốt nhất, gửi REPLICAOF NO ONE, rồi trỏ các bản sao còn lại sang máy mới. Phần còn lại là nút chỉnh duy nhất bạn có.
Nhưng khách hàng thấy lâu hơn
Tôi cho một khách hỏi Sentinel địa chỉ máy chính, ghi liên tục, và tự nối lại khi hỏng:
ghi thành công 2.053
thất bại 4
gián đoạn từ góc nhìn khách: 6,27 giây
6,27 giây, dài hơn 4,50 giây của Sentinel. Khoảng chênh là phần khách phải tự làm: nhận ra kết nối cũ đã chết (hết hạn chờ), hỏi lại địa chỉ, mở kết nối mới — đúng cái máy tính phải khởi động lại sau khi có điện. Sentinel công bố máy chính mới không có nghĩa khách biết ngay.
Đây là con số nên dùng khi tính SLA, không phải con số của Sentinel. Và nó phụ thuộc nhiều vào cấu hình khách: hạn chờ socket, chính sách thử lại, việc có đăng ký kênh +switch-master hay chỉ hỏi lại theo chu kỳ. Khách hàng tốt nhất đăng ký Pub/Sub kênh +switch-master để biết ngay khi có chuyển đổi thay vì chờ kết nối hỏng. Phần lớn thư viện hiện đại làm điều này; kiểm tra thư viện của bạn.
Vì sao ba Sentinel chứ không phải hai
Quorum 2 nghĩa là cần hai Sentinel đồng ý máy chính đã chết. Với hai Sentinel, mất một cái là không còn đủ quorum — và mất một Sentinel là chuyện rất dễ xảy ra cùng lúc với sự cố làm máy chính chết. Sự cố mạng thường không lịch sự chọn đúng một máy.
Ba Sentinel chịu được mất một. Năm chịu được mất hai. Quan trọng hơn số lượng: chúng phải nằm ở ba vùng hỏng khác nhau. Ba container Sentinel trên cùng một máy chủ vật lý — như cấu hình tôi dùng để đo — không cho bảo đảm nào cả; máy đó chết là cả ba chết. Và Sentinel không nên chạy trên chính các máy Redis nếu tránh được: máy Redis chết vì hết bộ nhớ sẽ kéo theo Sentinel trên đó.
Bốn tham số đáng hiểu
sentinel monitor mymaster <ip> <port> <quorum>
sentinel down-after-milliseconds mymaster 3000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
down-after là nút chỉnh tốc độ chính. Ngắn thì phát hiện nhanh, nhưng một đợt nghẽn mạng thoáng qua cũng đủ gây chuyển đổi không cần thiết — và chuyển đổi luôn tốn dữ liệu, như đo ở phần trước. 3–5 giây là khoảng thông thường; dưới 1 giây chỉ hợp với mạng rất ổn định.
parallel-syncs là số bản sao đồng bộ lại cùng lúc với máy chính mới. Đặt 1 giữ cho máy chính mới không bị đè bởi nhiều lần đồng bộ toàn phần — và như đo ở phần 18, mỗi lần đồng bộ toàn phần là một fork cộng chi phí copy-on-write. Với nhiều bản sao, đặt 1 và chấp nhận chờ lâu hơn.
failover-timeout là thời gian trước khi Sentinel bỏ cuộc và thử lại. Đặt quá ngắn thì nó bắt đầu lại giữa chừng.
Cái Sentinel không giải quyết
Nó không chống mất dữ liệu. Phần trước đo được: bản sao chậm thì mất đúng phần chậm đó. Sentinel chọn bản sao có offset cao nhất, nhưng "cao nhất trong số còn sống" không có nghĩa "đầy đủ".
Nó không giải quyết não phân đôi hoàn toàn. Máy chính cũ sống lại trong một phân vùng mạng vẫn có thể nhận ghi cho tới khi nó thấy Sentinel. Hai tham số giảm thiểu:
min-replicas-to-write 1
min-replicas-max-lag 10
Đặt trên chính Redis, không phải Sentinel: máy chính từ chối ghi nếu không có ít nhất một bản sao theo kịp trong 10 giây. Máy chính bị cô lập sẽ tự ngừng nhận ghi thay vì tích luỹ dữ liệu sẽ bị vứt.
Nó không san tải. Sentinel chỉ làm chuyển đổi. Muốn chia dữ liệu ra nhiều máy thì cần Cluster — phần sau của sê-ri.
Khách hàng phải nói chuyện với Sentinel
Đây là điểm hay bị bỏ qua khi triển khai: ứng dụng không kết nối thẳng tới địa chỉ Redis nữa. Nó hỏi Sentinel:
SENTINEL get-master-addr-by-name mymaster
rồi kết nối tới địa chỉ nhận được. Cấu hình trong ứng dụng phải là danh sách địa chỉ Sentinel, không phải địa chỉ Redis. Cấu hình sai chỗ này là lỗi phổ biến nhất: mọi thứ chạy hoàn hảo cho tới lần chuyển đổi đầu tiên, rồi ứng dụng vẫn cố nối tới máy cũ mãi mãi.
Mẫu số chung: thời gian phục hồi có nhiều lớp, và người dùng đo lớp ngoài cùng
Cái khoảng chênh 4,50 so với 6,27 giây là quy luật chung của mọi hệ tự chuyển đổi: thời gian phục hồi không phải một con số mà là chồng nhiều lớp trễ — phát hiện (chờ đủ chắc chắn để không báo động giả), quyết định và chuyển, rồi client nhận ra và nối lại. Cái mà bộ điều phối báo cáo chỉ là lớp giữa; cái người dùng cảm nhận là tổng cả ba, và luôn dài hơn. Đo lớp bên trong rồi hứa nó với SLA là hứa nhầm.
Trong thế giới Java, đây đúng là chuyện Spring Data Redis với Sentinel (Lettuce/Jedis): driver có nhận +switch-master nhanh đến đâu thì connection pool vẫn phải đuổi những kết nối chết và mở lại, nên downtime ứng dụng cảm nhận gồm cả thời gian evict + validate pool — không chỉ thời gian promote. Với một CSDL sau Patroni/PgBouncer cũng vậy: thời gian promote của orchestrator nhỏ hơn nhiều thời gian ứng dụng thật sự chạy lại được.
Trong Go, database/sql hay client Redis phải tự thử lại và phân giải lại địa chỉ khi kết nối hỏng — nếu client cache địa chỉ cũ (đúng bẫy "cấu hình địa chỉ Redis thay vì địa chỉ Sentinel"), nó nối vào máy chết mãi mãi dù hệ đã chuyển xong.
Còn ở cơ sở dữ liệu và hạ tầng, đây là mô hình HA quen thuộc: Patroni + etcd (hay ZooKeeper, Consul) làm bộ quyết định "ai còn sống", và nó cần số lẻ thành viên ở các vùng hỏng độc lập vì đúng lý do ba Sentinel — quá bán phải đạt được kể cả khi mất một, và ba cái trên cùng một máy thì mất máy là mất cả ba. Readiness probe của Kubernetes cộng việc Service trỏ lại endpoint cũng thêm đúng lớp trễ "phát hiện + định tuyến lại" ấy.
Mẫu số chung: thời gian phục hồi của một hệ tự chuyển đổi là chồng nhiều lớp — phát hiện, chuyển đổi, và client nối lại — nên con số bộ điều phối báo luôn nhỏ hơn downtime người dùng cảm nhận; và bộ quyết định "ai còn sống" cần số lẻ thành viên ở các vùng hỏng độc lập, nếu không nó vừa không đạt quorum khi cần vừa mở đường cho não phân đôi. Câu hỏi đúng cho SLA không phải "hệ chuyển đổi mất bao lâu" mà là "từ lúc máy chết đến lúc ứng dụng của tôi ghi lại được là bao lâu, và client của tôi có tự phân giải lại địa chỉ không". Đo ở phía client, không ở phía orchestrator.
Muốn tự kiểm Sentinel của bạn có thật sự sẵn sàng không:
redis-cli -p 26379 sentinel master mymaster | paste - - | \
grep -E 'flags|num-slaves|num-other-sentinels|quorum|down-after'
redis-cli -p 26379 sentinel ckquorum mymaster
SENTINEL CKQUORUM trả lời thẳng câu hỏi "nếu máy chính chết ngay bây giờ, có chuyển đổi được không". Nó kiểm cả quorum lẫn số Sentinel cần để bầu chọn. Ba dấu hiệu cần xem:
num-other-sentinelsnhỏ hơn bạn nghĩ — có Sentinel đã chết mà không ai để ý.num-slavesbằng 0 — không có gì để thăng cấp; Sentinel sẽ phát hiện máy chính chết rồi không làm gì được.ckquorumbáoNOQUORUM— hệ thống hiện đang không có khả năng chuyển đổi, và đó là thứ bạn muốn biết trước sự cố chứ không phải trong lúc nó.
Phần sau: Cluster — đo cách Redis chia dữ liệu ra nhiều máy và cái giá của việc đó.