Redis集群->拓撲結構
Redis 集群是一個網狀結構,無中心結構,每個節點都通過 TCP 連接跟其他每個節點連接。
在一個有 N 個節點的集群中,每個節點都有 N-1 個流出的 TCP 連接,和 N-1 個流入的連接。 這些 TCP 連接會永久保持,並不是按需創建的。
節點們使用一個 gossip 協議來傳播集群的信息,這樣可以:發現新的節點、 發送ping包(用來確保所有節點都在正常工作中)、在特定情況發生時發送集群消息。集群連接也用於在集群中發布或訂閱消息。

Redis集群->鍵分布模型
鍵空間被分割為 16384 槽(slot),事實上集群的最大節點數量是 16384 個。(然而建議最大節點數量設置在1000這個數量級上)
Hash Key如下:
HASH_SLOT = CRC16(key) mod 16384
舉個例子:
有三個Redis節點,節點1、2、3負責這些Slot范圍:0 - 5461,5462 - 10924,10924 - 16384

關於SLot的如何分配的?
官方提供了redis-trib的Ruby程序,其實就是集群管理工具,這個程序通過向實例發送特殊命令來完成創建新集群, 檢查集群, 或者對集群進行重新分片(reshared)等工作。Reshard是將Redis節點負現的Slot進行重分配,運維只需要發出Reshard指令,Reshard的過程是Redis集群自動進行的
Redis集群->客戶端實現
Moved重定向
一個 Redis 客戶端可以自由地向集群中的任意節點(包括從節點)發送查詢。接收的節點會分析查詢,如果這個命令是集群可以執行的(就是查詢中只涉及一個鍵),那么節點會找這個鍵所屬的哈希槽對應的節點。
如果剛好這個節點就是對應這個哈希槽,那么這個查詢就直接被節點處理掉。否則這個節點會查看它內部的 哈希槽 -> 節點ID 映射,然后給客戶端返回一個 MOVED 錯誤。
這個錯誤包括鍵(3999)的哈希槽和能處理這個查詢的節點的 ip:端口號(127.0.0.1:6381)。客戶端需要重新發送查詢到給定 ip 地址和端口號的節點。 注意,即使客戶端在重發查詢之前等待了很長一段時間,與此同時集群的配置信息發生改變,如果哈希槽 3999 現在是為其他節點服務,那么目標節點會再向客戶端回復一個 MOVED 錯誤。
當集群是穩定的時候,所有客戶端最終都會得到一份哈希槽 -> 節點的映射表,這樣能使得集群效率非常高:客戶端直接定位目標節點,不用重定向、或代理或發生其他單點故障
Ask重定向
為什么我們不能單純地使用 MOVED 重定向呢?因為當我們使用 MOVED 的時候,意味着我們認為哈希槽永久地被另一個不同的節點處理,並且希望接下來的所有查詢都嘗試發到這個指定的節點上去。而 ASK 意味着我們只要下一個查詢發送到指定節點上去。

上圖中,配置Slot1需要從節點A遷移到節點B,遷移過程中Key1、Key2已遷到節點B,而Key3、Key4仍在節點A。客戶端之前保存的映射表中Slot1 -> 節點A,所以客戶端來節點A來查詢Key1、Key2時,節點A已不負責這兩個Key,因而返回Ask重定向,通知客戶端去節點B去獲取數據。由於是Ask重定向,客戶並不會更新Slot1 -> 節點A的映射表(除非遷移過程完成),這樣在獲取Key3、Key4的數據時,客戶端可以一次性獲取到。
Redis集群->一致性保證
Redis 並不能保證數據的強一致性. 這意味這在實際中集群在特定的條件下可能會丟失寫操作.
- 原因是因為集群是用了異步復制. 寫操作過程:
客戶端向主節點B寫入一條命令.
主節點B向客戶端回復命令狀態.
主節點將寫操作復制給他得從節點 B1, B2 和 B3.
主節點對命令的復制工作發生在返回命令回復之后, 因為如果每次處理命令請求都需要等待復制操作完成的話, 那么主節點處理命令請求的速度將極大地降低 —— 我們必須在性能和一致性之間做出權衡。 注意:Redis 集群可能會在將來提供同步寫的方法。 -
Redis 集群另外一種可能會丟失命令的情況是集群出現了網絡分區, 並且一個客戶端與至少包括一個主節點在內的少數實例被孤立。
Redis集群->同步時鍾
Redis集群->集群階段,邏輯時鍾
本質上說,epoch 是一個集群里的邏輯時鍾,並決定一個給定的消息贏了另一個帶着更小 epoch 的消息。
,它是用來記錄事件的版本號,所以當有多個節點提供了沖突的信息的時候,另外的節點就可以通過這個狀態來了解哪個是最新的。 currentEpoch 是一個 64bit 的 unsigned 數。
Redis 集群中的每個節點,包括主節點和從節點,都在創建的時候設置了 currentEpoch 為0。
當節點接收到來自其他節點的 ping 包或 pong 包的時候,如果發送者的 epoch(集群連接消息頭部的一部分)大於該節點的 epoch,那么更新發送者的 epoch 為 currentEpoch。
由於這個語義,最終所有節點都會支持集群中較大的 epoch。
Redis集群->配置階段,配置時鍾
每一個主節點總是通過發送 ping 包和 pong 包向別人宣傳它的 configEpoch 和一份表示它負責的哈希槽的位圖。
當一個新節點被創建的時候,主節點中的 configEpoch 設為零。
從節點由於故障轉移事件被提升為主節點時,為了取代它那失效的主節點,會把 configEpoch 設置為它贏得選舉的時候的 configEpoch 值。
configEpoch 用於在不同節點提出不同的配置信息的時候(這種情況或許會在分區之后發生)解決沖突。(按我理解:ConfigEpoch是管配置更新的,有需要才去更新configEpoch,而像Epoch是定時包來更新)
從節點也會在 ping 包和 pong 包中向別人宣傳它的 configEpoch 域,不過從節點的這個域表示的是上一次跟它的主節點交換數據的時候主節點的 configEpoch 值。這能讓其他個體檢測出從節點的配置信息是不是需要更新了(主節點不會給一個配置信息過時的從節點投票)。
每次由於一些已知節點的值比自己的值大而更新 configEpoch 值,它都會永久性地存儲在 nodes.conf 文件中。
當一個節點重啟,它的 configEpoch 值被設為所有已知節點中最大的那個 configEpoch 值。
Redis集群->選舉機制
從節點的選舉和提升都是由從節點處理的,主節點會投票要提升哪個從節點。一個從節點的選舉是在主節點被至少一個具有成為主節點必備條件的從節點標記為 FAIL 的狀態的時候發生的。
當以下條件滿足時,一個從節點可以發起選舉:
- 該從節點的主節點處於 FAIL 狀態。
- 這個主節點負責的哈希槽數目不為零。
-
從節點和主節點之間的重復連接(replication link)斷線不超過一段給定的時間,這是為了確保從節點的數據是可靠的。
選舉完成后,從節點正式提升為主節點,並需要更新其在集群中負責的配置
Redis集群 -> 備份遷移
Redis 集群實現了一個叫做備份遷移(replica migration)的概念,以提高系統的可用性。在集群中有主節點-從節點的設定,如果主從節點間的映射關系是固定的,那么久而久之,當發生多個單一節點獨立故障的時候,系統可用性會變得很有限。(其實就是一主多備的機制,多備的機器可以復用給其它的主)

假設集群有兩個主節點 A,B。
1.節點 A 有一個從節點A1 。節點 B 有兩個從節點:B1 和 B2。
2.主節點 B 失效。B1 被提升為主節點。
3.節點 B2 遷移成為節點 A1 的從節點,要不然 A1 就沒有任何從節點。
4.三個小時后節點 A1 也失效了。
5.節點 B2 被提升為取代 A1 的新主節點。
集群仍然能繼續正常工作。選新備機的方法:找備機最多的主要