Redis——cluster集群原理
摘要
在 redis3.0之前,redis使用的哨兵架構,它借助 sentinel 工具來監控 master 節點的狀態;如果 master 節點異常,則會做主從切換,將一臺 slave 作為 master。當master掛掉的時候,sentinel 會選舉出來一個 master,選舉的時候是沒有辦法去訪問Redis的,會存在訪問瞬斷的情況;若是在電商網站大促的時候master給掛掉了,幾秒鐘損失好多訂單數據;哨兵模式,對外只有master節點可以寫,slave節點只能用于讀。盡管Redis單節點最多支持10W的QPS,但是在電商大促的時候,寫數據的壓力全部在master上。Redis的單節點內存不能設置過大,若數據過大在主從同步將會很慢;在節點啟動的時候,時間特別長;(從節點上有主節點的所有數據)。
一、redis集群架構與數據存儲原理
Redis集群是一個由多個主從節點群組成的分布式服務集群,它具有復制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成節點移除和故障轉移的功能。需要將每個節點設置成集群模式,這種集群模式沒有中心節點,可水平擴展,據官方文檔稱可以線性擴展到上萬個節點(官方推薦不超過1000個節點)。redis集群的性能和高可用性均優于之前版本的哨兵模式,且集群配置非常簡單。Redis 集群是一種分布式數據庫方案,集群通過分片(sharding)來進行數據管理(分治思想的一種實踐),并提供復制和故障轉移功能。將數據劃分為 16384 的 slots,每個節點負責一部分槽位。槽位的信息存儲于每個節點中。它是去中心化的,如圖所示,該集群有三個 Redis 節點組成,每個節點負責整個集群的一部分數據,每個節點負責的數據多少可能不一樣。三個節點相互連接組成一個對等的集群,它們之間通過?Gossip協議相互交互集群信息,最后每個節點都保存著其他節點的 slots 分配情況。???????
Redis集群的優點:
- Redis集群有多個master,可以減小訪問瞬斷問題的影響;若集群中有一個master掛了,正好需要向這個master寫數據,這個操作需要等待;但向其他master節點寫數據不受影響。
- Redis集群有多個master,可以提供更高的并發量;
- Redis集群可以分片存儲,這樣就可以存儲更多的數據;
使用 Redis Cluster 集群,主要解決了大數據量存儲導致的各種慢問題,同時也便于橫向拓展
兩種方案對應著 Redis 數據增多的兩種拓展方案:垂直擴展(scale up)、水平擴展(scale out)。
- 垂直拓展:升級單個 Redis 的硬件配置,比如增加內存容量、磁盤容量、使用更強大的 CPU。
- 水平拓展:橫向增加 Redis 實例個數,每個節點負責一部分數據。
比如需要一個內存 24 GB 磁盤 150 GB 的服務器資源,有以下兩種方案:
在面向百萬、千萬級別的用戶規模時,橫向擴展的 Redis 切片集群會是一個非常好的選擇。
- 垂直拓展部署簡單,但是當數據量大并且使用 RDB 實現持久化,會造成阻塞導致響應慢。另外受限于硬件和成本,拓展內存的成本太大,比如拓展到 1T 內存。
- 水平拓展便于拓展,同時不需要擔心單個實例的硬件和成本的限制。但是,切片集群會涉及多個實例的分布式管理問題,需要解決如何將數據合理分布到不同實例,同時還要讓客戶端能正確訪問到實例上的數據。
Redis Cluster 具有以下特點:
- 節點互通:所有的 Redis 節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬;
- 去中心化:Redis Cluster 不存在中心節點,每個節點都記錄有集群的狀態信息,并且通過 Gossip 協議,使每個節點記錄的信息實現最終一致性;
- 客戶端直連:客戶端與 Redis 節點直連,不需要中間 Proxy 層,客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可;
- 數據分片:Redis Cluster 的鍵空間被分割為 16384 個 Slot,這些 Slot 被分別指派給主節點,當存儲 Key-Value 時,根據 CRC16(key) Mod 16384的值,決定將一個 Key-Value 放到哪個 Slot 中;
- 多數派原則:對于集群中的任何一個節點,需要超過半數的節點檢測到它失效(pFail),才會將其判定為失效(Fail);
- 自動 Failover:當集群中某個主節點故障后(Fail),其它主節點會從故障主節點的從節點中選舉一個“最佳”從節點升主,替代故障的主節點;
- 功能弱化:集群模式下,由于數據分布在多個節點,不支持單機模式下的集合操作,也不支持多數據庫功能,集群只能使用默認的0號數據庫;
- 集群規模:官方推薦的最大節點數量為 1000 個左右,這是因為當集群規模過大時,Gossip 協議的效率會顯著下降,通信成本劇增。
1.1?redis數據分片原理
集群的整個數據庫被分為 16384 個槽(slot),數據庫中的每個鍵都屬于這 16384 個槽的其中一個,集群中的每個節點可以處理 0 個或最多 16384 個槽。
Key 與哈希槽映射過程可以分為兩大步驟:
- 根據鍵值對的 key,使用 CRC16 算法,計算出一個 16 bit 的值;
- 將 16 bit 的值對 16384 執行取模,得到 0 ~ 16383 的數表示 key 對應的哈希槽。
Cluster 還允許用戶強制某個 key 掛在特定槽位上,通過在 key 字符串里面嵌入 tag 標記,這就可以強制 key 所掛在的槽位等于 tag 所在的槽位。
1.2 Redis Cluster 請求路由方式
客戶端直連 Redis 服務,進行讀寫操作時,Key 對應的 Slot 可能并不在當前直連的節點上,經過“重定向”才能轉發到正確的節點。
和普通的查詢路由相比,Redis Cluster 借助客戶端實現的請求路由是一種混合形式的查詢路由,它并非從一個 Redis 節點到另外一個 Redis,而是借助客戶端轉發到正確的節點。實際應用中,可以在客戶端緩存 Slot 與 Redis 節點的映射關系,當接收到 MOVED 響應時修改緩存中的映射關系。如此,基于保存的映射關系,請求時會直接發送到正確的節點上,從而減少一次交互,提升效率。
客戶端又怎么確定訪問的數據到底分布在哪個實例上呢?
Redis 實例會將自己的哈希槽信息通過 Gossip 協議發送給集群中其他的實例,實現了哈希槽分配信息的擴散。這樣,集群中的每個實例都有所有哈希槽與實例之間的映射關系信息。
在切片數據的時候是將 key 通過 CRC16 計算出一個值再對 16384 取模得到對應的 Slot,這個計算任務可以在客戶端上執行發送請求的時候執行。但是,定位到槽以后還需要進一步定位到該 Slot 所在 Redis 實例。當客戶端連接任何一個實例,實例就將哈希槽與實例的映射關系響應給客戶端,客戶端就會將哈希槽與實例映射信息緩存在本地。當客戶端請求時,會計算出鍵所對應的哈希槽,在通過本地緩存的哈希槽實例映射信息定位到數據所在實例上,再將請求發送給對應的實例。
哈希槽與實例之間的映射關系由于新增實例或者負載均衡重新分配導致改變了咋辦?
集群中的實例通過 Gossip 協議互相傳遞消息獲取最新的哈希槽分配信息,但是,客戶端無法感知。Redis Cluster 提供了重定向機制:客戶端將請求發送到實例上,這個實例沒有相應的數據,該 Redis 實例會告訴客戶端將請求發送到其他的實例上。
Redis 如何告知客戶端重定向訪問新實例呢?分為兩種情況:MOVED 錯誤、ASK 錯誤。
MOVED 錯誤:
MOVED 錯誤(負載均衡,數據已經遷移到其他實例上):當客戶端將一個鍵值對操作請求發送給某個實例,而這個鍵所在的槽并非由自己負責的時候,該實例會返回一個 MOVED 錯誤指引轉向正在負責該槽的節點。
(error) MOVED 16330 172.17.18.2:6379
該響應表示客戶端請求的鍵值對所在的哈希槽 16330 遷移到了 172.17.18.2 這個實例上,端口是 6379。這樣客戶端就與 172.17.18.2:6379 建立連接,并發送 GET 請求。同時,客戶端還會更新本地緩存,將該 slot 與 Redis 實例對應關系更新正確。
ASK 錯誤:如果某個 slot 的數據比較多,部分遷移到新實例,還有一部分沒有遷移咋辦?
如果請求的 key 在當前節點找到就直接執行命令,否則時候就需要 ASK 錯誤響應了,槽部分遷移未完成的情況下,如果需要訪問的 key 所在 Slot 正在從從 實例 1 遷移到 實例 2,實例 1 會返回客戶端一條 ASK 報錯信息:客戶端請求的 key 所在的哈希槽正在遷移到實例 2 上,你先給實例 2 發送一個 ASKING 命令,接著發發送操作命令。
(error) ASK 16330 172.17.18.2:6379
比如客戶端請求定位到 key的槽16330 在實例 172.17.18.1 上,節點1如果找得到就直接執行命令,否則響應 ASK 錯誤信息,并指引客戶端轉向正在遷移的目標節點 172.17.18.2。
注意:ASK 錯誤指令并不會更新客戶端緩存的哈希槽分配信息。所以客戶端再次請求 Slot 16330 的數據,還是會先給 172.17.18.1 實例發送請求,只不過節點會響應 ASK 命令讓客戶端給新實例發送一次請求。MOVED指令則更新客戶端本地緩存,讓后續指令都發往新實例。
1.3 Redis的一致性哈希算法
采用一致性哈希算法(consistent hashing),將key和節點name同時hashing,然后進行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由于重新匹配造成的rehashing。一致性哈希只影響相鄰節點key分配,影響量小。
為了避免一致性哈希只影響相鄰節點造成節點分配壓力,ShardedJedis會對每個Redis節點根據名字(沒有,Jedis會賦予缺省名字)會虛擬化出160個虛擬節點進行散列。根據權重weight,也可虛擬化出160倍數的虛擬節點。用虛擬節點做映射匹配,可以在增加或減少Redis節點時,key在各Redis節點移動再分配更均勻,而不是只有相鄰節點受影響。
Hash環的數據傾斜問題:一致性Hash算法在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜(被緩存的對象大部分集中緩存在某一臺服務器上)問題,例如系統中只有兩臺服務器,其環分布如圖所示,此時必然造成大量數據集中到Node A上,而只有極少量會定位到Node B上。為了解決這種數據傾斜問題,一致性Hash算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱為虛擬節點。具體做法可以在服務器IP或主機名的后面增加編號來實現。例如上面的情況,可以為每臺服務器計算三個虛擬節點,于是可以分別計算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六個虛擬節點:
二、redis集群選舉算法
2.1 集群初始選舉算法
- 集群的配置紀元 +1,是一個自曾計數器,初始值 0 ,每次執行故障轉移都會 +1。
- 檢測到主節點下線的從節點向集群廣播一條
CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息、并且具有投票權的主節點向這個從節點投票。 - 這個主節點尚未投票給其他從節點,那么主節點將向要求投票的從節點返回一條
CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示這個主節點支持從節點成為新的主節點。 - 參與選舉的從節點都會接收
CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,如果收集到的票 >= (N/2) + 1 支持,那么這個從節點就被選舉為新主節點。 - 如果在一個配置紀元里面沒有從節點能收集到足夠多的支持票,那么集群進入一個新的配置紀元,并再次進行選舉,直到選出新的主節點為止。
跟哨兵類似,兩者都是基于 Raft 算法來實現的,流程如圖所示:
三、redis集群通信原理
Redis 自3.0版本起,支持 Redis Cluster,真正意義上實現了分布式。在分布式系統中,節點間的通信十分重要,是構建集群的基石。那么 Redis Cluster 中,節點間是如何通信的呢?又是如何保障一致性、可用性的呢?欲知答案,必先了解 Gossip 算法。Gossip 算法源自流行病學的研究,經過不斷的發展演化,作為一種分布式一致性協議而得到廣泛應用,如 Cassandra、Akka、Redis 都有用到。
Gossip 特點:在一個有界網絡中,每個節點都隨機地與其它節點通信,經過一番雜亂無章的通信,最終所有節點的狀態都會達成一致。每個節點可能知道所有其它節點,也可能僅知道幾個鄰居節點,只要這些節可以通過網絡連通,最終它們的狀態都是一致的。要注意到的一點是,即使有的節點因宕機而重啟,有新節點加入,但經過一段時間后,這些節點的狀態也會與其他節點達成一致,也就是說,Gossip 天然具有分布式容錯的優點。
Gossip 是一個帶冗余的容錯算法,更進一步,Gossip 是一個最終一致性算法。雖然無法保證在某個時刻所有節點狀態一致,但可以保證在“最終”所有節點一致,“最終”是一個現實中存在,但理論上無法證明的時間點。因為 Gossip 不要求節點知道所有其它節點,因此又具有去中心化的特點,節點之間完全對等,不需要任何的中心節點。實際上 Gossip 可以用于眾多能接受“最終一致性”的領域:失敗檢測、路由同步、Pub/Sub、動態負載均衡。但 Gossip 的缺點也很明顯,冗余通信會對網路帶寬、CUP 資源造成很大的負載,而這些負載又受限于通信頻率,該頻率又影響著算法收斂的速度,
Gossip 在 Redis Cluster 中的作用:
在分布式系統中,需要提供維護節點元數據信息的機制,所謂元數據是指節點負責哪些數據、主從屬性、是否出現故障等狀態信息。常見的元數據維護方式分為集中式和無中心式。Redis Cluster 采用 Gossip 協議實現了無中心式。Redis Cluster 中使用 Gossip 主要有兩大作用:
- 去中心化,以實現分布式和彈性擴展;
- 失敗檢測,以實現高可用;
3.1 Gossip 消息種類
發送的消息結構是 clusterMsgDataGossip結構體組成:
typedef struct {char nodename[CLUSTER_NAMELEN]; //40字節uint32_t ping_sent; //4字節uint32_t pong_received; //4字節char ip[NET_IP_STR_LEN]; //46字節uint16_t port; //2字節uint16_t cport; //2字節uint16_t flags; //2字節uint32_t notused1; //4字節
} clusterMsgDataGossip;
所以每個實例發送一個 Gossip消息,就需要發送 104 字節。如果集群是 1000 個實例,那么每個實例發送一個 PING 消息則會占用 大約 10KB。除此之外,實例間在傳播 Slot 映射表的時候,每個消息還包含了 一個長度為 16384 bit 的 Bitmap。每一位對應一個 Slot,如果值 = 1 則表示這個 Slot 屬于當前實例,這個 Bitmap 占用 2KB,所以一個 PING 消息大約 12KB。PONG與PING 消息一樣,一發一回兩個消息加起來就是 24 KB。集群規模的增加,心跳消息越來越多就會占據集群的網絡通信帶寬,降低了集群吞吐量。
Gossip 協議的主要職責就是信息交換。信息交換的載體就是節點彼此發送的Gossip 消息,常用的 Gossip 消息可分為:Ping 消息、Pong 消息、Meet 消息、Fail 消息。
- Meet 消息:用于通知新節點加入。消息發送者通知接收者加入到當前集群,Meet 消息通信正常完成后,接收節點會加入到集群中并進行周期性的 Ping、Pong 消息交換;
- Ping 消息:集群內交換最頻繁的消息,集群內每個節點每秒向多個其它節點發送 Ping 消息,用于檢測節點是否在線和交換彼此狀態信息。Ping 消息發送封裝了自身節點和部分其它節點的狀態數據;
- Pong 消息:當接收到 Ping、Meet 消息時,作為響應消息回復給發送方確認消息正常通信。Pong 消息內部封裝了自身狀態數據。節點也可以向集群內廣播自身的 Pong 消息來通知整個集群對自身狀態進行更新;
- Fail 消息:當節點判定集群內另一個節點下線時,會向集群內廣播一個 Fail 消息,其他節點接收到 Fail 消息之后把對應節點更新為下線狀態。
由于集群內部需要頻繁地進行節點信息交換,而 Ping/Pong 消息攜帶當前節點和部分其它節點的狀態數據,勢必會加重帶寬和計算的負擔。Redis 集群內節點通信采用固定頻率(定時任務每秒執行10次),因此,節點每次選擇需要通信的節點列表變得非常重要。通信節點選擇過多雖然可以做到信息及時交換但成本過高。節點選擇過少則會降低集群內所有節點彼此信息交換的頻率,從而影響故障判定、新節點發現等需求的速度。因此 Redis 集群的 Gossip 協議需要兼顧信息交換實時性和成本開銷。
發送 PING 消息的頻率也會影響集群帶寬吧?
Redis Cluster 的實例啟動后,默認會每秒從本地的實例列表中隨機選出 5 個實例,再從這 5 個實例中找出一個最久沒有收到 PING 消息的實例,把 PING 消息發送給該實例。
隨機選擇 5 個,但是無法保證選中的是整個集群最久沒有收到 PING 通信的實例,有的實例可能一直沒有收到消息,導致他們維護的集群信息早就過期了,咋辦呢?
這個問題問的好,Redis Cluster 的實例每 100 ms 就會掃描本地實例列表,當發現有實例最近一次收到 PONG 消息的時間 > cluster-node-timeout / 2。那么就立刻給這個實例發送 PING 消息,更新這個節點的集群狀態信息。當集群規模變大,就會進一步導致實例間網絡通信延遲怎加。可能會引起更多的 PING 消息頻繁發送。
3.1.1 降低實例間的通信開銷
- 每個實例每秒發送一條
PING消息,降低這個頻率可能會導致集群每個實例的狀態信息無法及時傳播。 - 每 100 ms 檢測實例
PONG消息接收是否超過cluster-node-timeout / 2,這個是 Redis 實例默認的周期性檢測任務頻率,我們不會輕易修改。
所以,只能修改 cluster-node-timeout的值:集群中判斷實例是否故障的心跳時間,默認 15 S。所以,為了避免過多的心跳消息占用集群寬帶,將 cluster-node-timeout調成 20 秒或者 30 秒,這樣 PONG 消息接收超時的情況就會緩解。但是,也不能設置的太大。都則就會導致實例發生故障了,等待 cluster-node-timeout時長才能檢測出這個故障,影響集群正常服務、
五、redis集群的故障轉移原理
如果某個主節點沒有從節點,那么當它發生故障時,集群將完全處于不可用狀態。
不過 Redis 也提供了一個參數cluster-require-full-coverage可以允許部分節點故障,其它節點還可以繼續提供對外訪問。比如 7000 主節點宕機,作為 slave 的 7003 成為 Master 節點繼續提供服務。當下線的節點 7000 重新上線,它將成為當前 70003 的從節點。
5.1 故障檢測
一個節點認為某個節點失聯了并不代表所有的節點都認為它失聯了。只有當大多數負責處理 slot 節點都認定了某個節點下線了,集群才認為該節點需要進行主從切換。Redis 集群節點采用 Gossip協議來廣播自己的狀態以及自己對整個集群認知的改變。比如一個節點發現某個節點失聯了 (PFail),它會將這條信息向整個集群廣播,其它節點也就可以收到這點失聯信息。
如果一個節點收到了某個節點失聯的數量 (PFail Count) 已經達到了集群的大多數,就可以標記該節點為確定下線狀態 (Fail),然后向整個集群廣播,強迫其它節點也接收該節點已經下線的事實,并立即對該失聯節點進行主從切換。
5.2 故障轉移
當一個 Slave 發現自己的主節點進入已下線狀態后,從節點將開始對下線的主節點進行故障轉移。
- 從下線的 Master 及節點的 Slave 節點列表選擇一個節點成為新主節點。
- 新主節點會撤銷所有對已下線主節點的 slot 指派,并將這些 slots 指派給自己。
- 新的主節點向集群廣播一條 PONG 消息,這條 PONG 消息可以讓集群中的其他節點立即知道這個節點已經由從節點變成了主節點,并且這個主節點已經接管了原本由已下線節點負責處理的槽。
- 新的主節點開始接收處理槽有關的命令請求,故障轉移完成。
5.3 選主流程
- 集群的配置紀元 +1,是一個自曾計數器,初始值 0 ,每次執行故障轉移都會 +1。
- 檢測到主節點下線的從節點向集群廣播一條
CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息、并且具有投票權的主節點向這個從節點投票。 - 這個主節點尚未投票給其他從節點,那么主節點將向要求投票的從節點返回一條
CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示這個主節點支持從節點成為新的主節點。 - 參與選舉的從節點都會接收
CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,如果收集到的票 >= (N/2) + 1 支持,那么這個從節點就被選舉為新主節點。 - 如果在一個配置紀元里面沒有從節點能收集到足夠多的支持票,那么集群進入一個新的配置紀元,并再次進行選舉,直到選出新的主節點為止。
跟哨兵類似,兩者都是基于 Raft 算法來實現的,流程如圖所示:
六、redis集群擴容與縮容原理
隨著應用場景的升級,緩存可能需要擴容,擴容的方式有兩種:垂直擴容(Scale Up)和水平擴容(Scale Out)。垂直擴容無需詳述。實際應用場景中,采用水平擴容更多一些,根據是否增加主節點數量,水平擴容方式有兩種。
6.1 Redis集群的擴容方式
主節點數量不變
比如,當前有一臺物理機 A,構建了一個包含3個 Redis 實例的集群;擴容時,我們新增一臺物理機 B,拉起一個 Redis 實例并加入物理機 A 的集群;B 上 Redis 實例對 A 上的一個主節點進行復制,然后進行主備倒換;如此,Redis 集群還是3個主節點,只不過變成了 A2-B1 的結構,將一部分請求壓力分擔到了新增的節點上,同時物理容量上限也會增加,主要步驟如下:
- 將新增節點加入集群;
- 將新增節點設置為某個主節點的從節點,進而對其進行復制;
- 進行主備倒換,將新增的節點調整為主。
增加主節點數量。
不增加主節點數量的方式擴容比較簡單,但是,從負載均衡的角度來看,并不是很好的選擇。例如,如果主節點數量較少,那么單個節點所負責的 Slot 的數量必然較多,很容易出現大量 Key 的讀寫集中于少數節點的現象,而增加主節點的數量,可以更有效的分攤訪問壓力,充分利用資源。主要步驟如下:
- 將新增節點加入集群;
- 將集群中的部分 Slot 遷移至新增的節點。
6.2 Redis集群的擴容原理
新節點剛開始都是master節點,但是由于沒有負責的槽,所以不能接收任何讀寫操作,對新節點的后續操作,一般有兩種選擇:
- 從其他的節點遷移槽和數據給新節點
- 作為其他節點的slave負責故障轉移
# 新節點加入集群
redis-trib.rb add-node new_host:new_port old_host:old_port
# 新節點加入集群并作為指定master的slave
redis-trib.rb add-node new_host:new_port old_host:old_port --slave --master-id <master-id>
建議使用redis-trib.rb add-node將新節點添加到集群中,該命令會檢查新節點的狀態,如果新節點已經加入了其他集群或者已經包含數據,則會報錯,而使用cluster meet命令則不會做這樣的檢查,假如新節點已經存在數據,則會合并到集群中,造成數據不一致。
6.2.1 遷移slot和數據
- 假設原有3個master,每個master負責10384 / 3 ≈ 5461個slot
- 加入一個新的master之后,每個master負責10384 / 4 = 4096個slot
- 確定好遷移計劃之后,例如,每個master將超過4096個slot的部分遷移到新的master中,然后開始以slot為單位進行遷移。
?slot遷移的其他說明
- 遷移過程是同步的,在目標節點執行restore指令到原節點刪除key之間,原節點的主線程處于阻塞狀態,直到key被刪除成功
- 如果遷移過程突然出現網路故障,整個slot遷移只進行了一半,這時兩個節點仍然會被標記為中間過濾狀態,即"migrating"和"importing",下次遷移工具連接上之后,會繼續進行遷移
- 在遷移過程中,如果每個key的內容都很小,那么遷移過程很快,不會影響到客戶端的正常訪問
- 如果key的內容很大,由于遷移一個key的遷移過程是阻塞的,就會同時導致原節點和目標節點的卡頓,影響集群的穩定性,所以,集群環境下,業務邏輯要盡可能的避免大key的產生
6.3 Redis集群的縮容
- 如果下線的是slave,那么通知其他節點忘記下線的節點
- 如果下線的是master,那么將此master的slot遷移到其他master之后,通知其他節點忘記此master節點
- 其他節點都忘記了下線的節點之后,此節點就可以正常停止服務了
七、Redis集群總結
- 哨兵集群實現故障轉移,但是當數據量過大導致生成 RDB 時間過長。而 Fork 執行的時候會阻塞主線程,由于數據量過大導致阻塞主線程過長,所以出現了 Redis 響應慢的表象。
- 使用 Redis Cluster 集群,主要解決大數據量存儲導致的各種慢問題,同時也便于橫向拓展。面向百萬、千萬級別的用戶規模時,橫向擴展的 Redis 切片集群會是一個非常好的選擇。
- 集群的整個數據庫被分為 16384 個槽(slot),數據庫中的每個鍵都屬于這 16384 個槽的其中一個,集群中的每個節點可以處理 0 個或最多 16384 個槽。
- Redis 集群節點采用 Gossip 協議來廣播自己的狀態以及自己對整個集群認知的改變。
- 客戶端連接到集群候任何一個實例后,實例會將哈希槽與實例映射信息發送給客戶端,客戶端將信息保存,用于將 key 定位到對應的節點。
- 集群并不能無限增加,由于集群通過
Gossip協議傳播集群實例信息,所以通信頻率是限制集群大小的主要原因,主要可以通過修改cluster-node-timeout調整頻率。
7.1 阿里云Redis集群版
云數據庫 Redis 提供集群版實例,輕松突破 Redis 自身單線程瓶頸,可極大滿足對于 Redis 大容量或高性能的業務需求。 云數據庫 Redis 集群版內置數據分片及讀取算法,整體過程對用戶透明,免去用戶開發及運維 Redis 集群的煩惱。
7.1.1 使用場景
- QPS 壓力較大
標準版 Redis 無法支撐較大的 QPS,需要采用多節點的部署方式來沖破 Redis 單線程的性能瓶頸。Redis 集群版提供16、32、64、128、256 GB 五款集群版配置,提供8節點及16節點的部署模式。相對標準版可以將 QPS 提升8倍或16倍。 - 數據量較大
Redis 集群版可以有效的擴展數據量大小,相比標準版支持存儲量更大的64、128G、256 GB 集群版,可以有效的滿足數據擴展需求。 - 吞吐密集型應用
相比標準版,Redis 集群版的內網吞吐限制相對較松,針對熱點數據讀取、大吞吐類型的業務可以友好的支持。 - 對 Redis 協議不敏感的應用
由于集群版的架構引入了多個組件,在 Redis 協議支持上相比標準版有一定限制。
7.1.2 產品架構
云數據庫 Redis集群版實例由 Proxy 服務器(服務代理)、分片服務器(數據節點)和配置服務器(分區策略存儲)三個組件組成。
- Proxy 服務器:單節點配置,集群版結構中會有多個 Proxy 組成,系統會自動對其實現負載均衡及故障轉移。
- 分片服務器:分片服務器分為兩種架構,雙節點和單節點架構
雙節點:每個分片服務器均是雙副本高可用架構,主節點故障之后,系統會自動進行主備切換保證服務高可用。
單節點:每個分片服務器是單節點架構,沒有備用節點實時同步數據,不提供數據持久化和備份策略,數據節點故障之后,系統會在30秒內重新拉起一個 Redis 進程保證服務高可用,但是該節點的數據將會丟失掉。
- 配置服務器:用于存儲集群配置信息及分區策略,目前采用雙副本高可用架構,保證高可用。
阿里云的redis集群版由3大組件構成:
- redis-config : 集群管理工具
- redis-server : 優化過源碼的redis,支持slot, 擴容遷移等
- redis-proxy : 單線程,c++14語言實現的內核
redis-proxy 無狀態,一個集群根據集群規格可掛多個proxy節點。
redis-config 雙節點,支持容災。
集群元數據存儲在rds db上。
提供獨立的組件HA負責集群的主備切換等。
阿里云的redis集群同樣基于proxy,用戶對路由信息無感知,同時提供vip給客戶端訪問,客戶端只需一個連接地址即可,無須關心proxy訪問的負載均衡等。
7.1.3 性能對比
在3臺物理機上分別搭建了以上3種redis集群。每臺物理機千兆網卡,24核cpu,內存189G。3臺物理機分別跑壓測工具memtier_benchmark、codis proxy/阿里云proxy、redis server。redis server使用各種集群配套的redis內核。
固定key size 32個字節,set/get 操作比例為1:10。每個線程16個客戶端。連續壓測5分鐘,分8個, 16個, 32個, 48個, 64個線程壓測。因為redis4.0集群需要額外的客戶端選擇節點,而memtier_benchmark不支持,所以使用了hashtag 來壓測redis4.0。每個集群有8個master db, 8個slave db, aof打開。aof rewrite的最小buffer為64MB。壓測的對象分別為單個redis 4.0 節點, 單個阿里云redis-proxy, 單核的codis-proxy, 8核的codis-proxy。codis 使用的go版本為1.7.4。
可看出,單核的codis-proxy性能最弱。8核的codis-proxy壓測沒有對key使用hashtag,如此相當于將請求分散到后端8個db節點上, 也可以說相當于8個阿里云的redis-proxy。自然性能數據就比較高了。
單核的阿里云redis-proxy在壓力夠大的情況下性能逼近原生的redis db節點。在實際生產環境中,使用原生的redis cluster,客戶端需要實現cluster protocol, 解析move, ask等指令并重定向節點,隨意訪問key可能需要兩次訪問操作才能完成,性能上并不能完全如單節點一樣。
| redis 4.0 | 阿里云redis | codis | |
| 事務 | 支持相同slot | 支持相同的slot | 不支持 |
| sub/pub | 支持相同slot | 支持 | 不支持 |
| flushall | 支持 | 支持 | 不支持 |
| select | 不支持 | 支持 | 不支持 |
| mset/mget | 支持相同slot | 支持相同的slot | 不支持 |
7.1.4 水平擴展
redis4.0 cluster,codis,阿里云redis 分布式集群均實現了面對slot的管理,擴展的最小單元是slot。分布式集群中水平擴展的本質是對集群節點的路由信息管理以及數據的遷移。這3種集群遷移數據的最小單位均是key。
博文參考
redis4.0、codis、阿里云redis 3種redis集群對比分析-阿里云開發者社區
病毒入侵:全靠分布式
阿里云redis集群版-和阿里云redis集群版相關的內容-阿里云開發者社區
Redis 高可用篇:Cluster 集群能支持的數據量有多大? - SegmentFault 思否
總結
以上是生活随笔為你收集整理的Redis——cluster集群原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析STL allocator
- 下一篇: Ubuntu16.04搭建NFS 文件共