【原創】爲何Redis集羣有16384個槽

引言

我在《那些年用過的Redis集羣架構(含面試解析)》一文裏提到過,如今redis集羣架構,redis cluster用的會比較多。
以下圖所示

對於客戶端請求的key,根據公式HASH_SLOT=CRC16(key) mod 16384,計算出映射到哪一個分片上,而後Redis會去相應的節點進行操做!html

那你們思考過,爲何有16384個槽麼?
ps:CRC16算法產生的hash值有16bit,該算法能夠產生2^16-=65536個值。換句話說,值是分佈在0~65535之間。那做者在作mod運算的時候,爲何不mod65536,而選擇mod16384?node

其實我當初第一次思考這個問題的時候,我內心是這麼想的,做者應該是以爲16384就夠了,而後我就開始查這方面資料。git

很幸運的是,這個問題,做者是給出了回答的!
地址以下:
https://github.com/antirez/redis/issues/2576github

做者原版回答以下:
The reason is:面試

  • Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots.
  • At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs.

So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set.redis

所以,能看懂上面那段話的讀者。這篇文章不用看了,由於做者講的很清楚了。本文只是對上面那段話作一些解釋而已。算法

正文

基礎

咱們回憶一下Redis Cluster的工做原理!
這裏要先將節點握手講清楚。咱們讓兩個redis節點之間進行通訊的時候,須要在客戶端執行下面一個命令數組

127.0.0.1:7000>cluster meet 127.0.0.1:7001

以下圖所示
網絡

意思很簡單,讓7000節點和7001節點知道彼此存在!
在握手成功後,連個節點之間會按期發送ping/pong消息,交換數據信息,以下圖所示。
架構

在這裏,咱們須要關注三個重點。

  • (1)交換什麼數據信息
  • (2)數據信息究竟多大
  • (3)按期的頻率什麼樣

到底在交換什麼數據信息?
交換的數據信息,由消息體和消息頭組成。
消息體無外乎是一些節點標識啊,IP啊,端口號啊,發送時間啊。這與本文關係不是太大,我不細說。
咱們來看消息頭,結構以下

注意看紅框的內容,type表示消息類型。
另外,消息頭裏面有個myslots的char數組,長度爲16383/8,這實際上是一個bitmap,每個位表明一個槽,若是該位爲1,表示這個槽是屬於這個節點的。

到底數據信息究竟多大?
在消息頭中,最佔空間的是myslots[CLUSTER_SLOTS/8]。這塊的大小是:
16384÷8÷1024=2kb
那在消息體中,會攜帶必定數量的其餘節點信息用於交換。
那這個其餘節點的信息,究竟是幾個節點的信息呢?
約爲集羣總節點數量的1/10,至少攜帶3個節點的信息。
這裏的重點是:節點數量越多,消息體內容越大。

消息體大小是10個節點的狀態信息約1kb。

那按期的頻率是什麼樣的?
redis集羣內節點,每秒都在發ping消息。規律以下

  • (1)每秒會隨機選取5個節點,找出最久沒有通訊的節點發送ping消息
  • (2)每100毫秒(1秒10次)都會掃描本地節點列表,若是發現節點最近一次接受pong消息的時間大於cluster-node-timeout/2 則馬上發送ping消息

所以,每秒單節點發出ping消息數量爲
數量=1+10*num(node.pong_received>cluster_node_timeout/2)

那大體帶寬損耗以下所示,圖片來自《Redis運維與實現》

講完基礎知識之後,咱們能夠來看做者的回答了。

回答

(1)若是槽位爲65536,發送心跳信息的消息頭達8k,發送的心跳包過於龐大。
如上所述,在消息頭中,最佔空間的是myslots[CLUSTER_SLOTS/8]
當槽位爲65536時,這塊的大小是:
65536÷8÷1024=8kb
由於每秒鐘,redis節點須要發送必定數量的ping消息做爲心跳包,若是槽位爲65536,這個ping消息的消息頭太大了,浪費帶寬。
(2)redis的集羣主節點數量基本不可能超過1000個。
如上所述,集羣節點越多,心跳包的消息體內攜帶的數據越多。若是節點過1000個,也會致使網絡擁堵。所以redis做者,不建議redis cluster節點數量超過1000個。
那麼,對於節點數在1000之內的redis cluster集羣,16384個槽位夠用了。沒有必要拓展到65536個。
(3)槽位越小,節點少的狀況下,壓縮比高
Redis主節點的配置信息中,它所負責的哈希槽是經過一張bitmap的形式來保存的,在傳輸過程當中,會對bitmap進行壓縮,可是若是bitmap的填充率slots / N很高的話(N表示節點數),bitmap的壓縮率就很低。
若是節點數不多,而哈希槽數量不少的話,bitmap的壓縮率就很低。

ps:文件壓縮率指的是,文件壓縮先後的大小比。

綜上所述,做者決定取16384個槽,很少很多,剛恰好!

總結

但願你們有所收穫!

相關文章
相關標籤/搜索