引言
我在《那些年用過(guò)的Redis集群架構(gòu)(含面試解析)》 <https://www.cnblogs.com/rjzheng/p/10360619.html>
一文里提到過(guò),現(xiàn)在redis集群架構(gòu),redis cluster用的會(huì)比較多。
如下圖所示
對(duì)于客戶端請(qǐng)求的key,根據(jù)公式HASH_SLOT=CRC16(key) mod 16384,計(jì)算出映射到哪個(gè)分片上,然后Redis會(huì)去相應(yīng)的節(jié)點(diǎn)進(jìn)行操作!
那大家思考過(guò),為什么有16384個(gè)槽么?
ps:CRC16算法產(chǎn)生的hash值有16bit,該算法可以產(chǎn)生2^16-=65536個(gè)值。換句話說(shuō),值是分布在0~65535之間。那作者在做mod
運(yùn)算的時(shí)候,為什么不mod65536,而選擇mod16384?
其實(shí)我當(dāng)初第一次思考這個(gè)問(wèn)題的時(shí)候,我心里是這么想的,作者應(yīng)該是覺(jué)得16384就夠了,然后我就開(kāi)始查這方面資料。
很幸運(yùn)的是,這個(gè)問(wèn)題,作者是給出了回答的!
地址如下:
https://github.com/antirez/redis/issues/2576
作者原版回答如下:
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.
因此,能看懂上面那段話的讀者。這篇文章不用看了,因?yàn)樽髡咧v的很清楚了。本文只是對(duì)上面那段話做一些解釋而已。
正文
基礎(chǔ)
我們回憶一下Redis Cluster的工作原理!
這里要先將節(jié)點(diǎn)握手講清楚。我們讓兩個(gè)redis節(jié)點(diǎn)之間進(jìn)行通信的時(shí)候,需要在客戶端執(zhí)行下面一個(gè)命令
127.0.0.1:7000>cluster meet 127.0.0.1:7001
如下圖所示
意思很簡(jiǎn)單,讓7000節(jié)點(diǎn)和7001節(jié)點(diǎn)知道彼此存在!
在握手成功后,連個(gè)節(jié)點(diǎn)之間會(huì)定期發(fā)送ping/pong消息,交換數(shù)據(jù)信息,如下圖所示。
在這里,我們需要關(guān)注三個(gè)重點(diǎn)。
* (1)交換什么數(shù)據(jù)信息
* (2)數(shù)據(jù)信息究竟多大
* (3)定期的頻率什么樣
到底在交換什么數(shù)據(jù)信息?
交換的數(shù)據(jù)信息,由消息體和消息頭組成。
消息體無(wú)外乎是一些節(jié)點(diǎn)標(biāo)識(shí)啊,IP啊,端口號(hào)啊,發(fā)送時(shí)間啊。這與本文關(guān)系不是太大,我不細(xì)說(shuō)。
我們來(lái)看消息頭,結(jié)構(gòu)如下
注意看紅框的內(nèi)容,type表示消息類(lèi)型。
另外,消息頭里面有個(gè)myslots的char數(shù)組,長(zhǎng)度為16383/8,這其實(shí)是一個(gè)bitmap,每一個(gè)位代表一個(gè)槽,如果該位為1,表示這個(gè)槽是屬于這個(gè)節(jié)點(diǎn)的。
到底數(shù)據(jù)信息究竟多大?
在消息頭中,最占空間的是myslots[CLUSTER_SLOTS/8]。這塊的大小是:
16384÷8÷1024=2kb
那在消息體中,會(huì)攜帶一定數(shù)量的其他節(jié)點(diǎn)信息用于交換。
那這個(gè)其他節(jié)點(diǎn)的信息,到底是幾個(gè)節(jié)點(diǎn)的信息呢?
約為集群總節(jié)點(diǎn)數(shù)量的1/10,至少攜帶3個(gè)節(jié)點(diǎn)的信息。
這里的重點(diǎn)是:節(jié)點(diǎn)數(shù)量越多,消息體內(nèi)容越大。
消息體大小是10個(gè)節(jié)點(diǎn)的狀態(tài)信息約1kb。
那定期的頻率是什么樣的?
redis集群內(nèi)節(jié)點(diǎn),每秒都在發(fā)ping消息。規(guī)律如下
* (1)每秒會(huì)隨機(jī)選取5個(gè)節(jié)點(diǎn),找出最久沒(méi)有通信的節(jié)點(diǎn)發(fā)送ping消息
* (2)每100毫秒(1秒10次)都會(huì)掃描本地節(jié)點(diǎn)列表,如果發(fā)現(xiàn)節(jié)點(diǎn)最近一次接受pong消息的時(shí)間大于cluster-node-timeout/2
則立刻發(fā)送ping消息
因此,每秒單節(jié)點(diǎn)發(fā)出ping消息數(shù)量為
數(shù)量=1+10*num(node.pong_received>cluster_node_timeout/2)
那大致帶寬損耗如下所示,圖片來(lái)自《Redis運(yùn)維與實(shí)現(xiàn)》
講完基礎(chǔ)知識(shí)以后,我們可以來(lái)看作者的回答了。
回答
(1)如果槽位為65536,發(fā)送心跳信息的消息頭達(dá)8k,發(fā)送的心跳包過(guò)于龐大。
如上所述,在消息頭中,最占空間的是myslots[CLUSTER_SLOTS/8]。
當(dāng)槽位為65536時(shí),這塊的大小是:
65536÷8÷1024=8kb
因?yàn)槊棵腌?,redis節(jié)點(diǎn)需要發(fā)送一定數(shù)量的ping消息作為心跳包,如果槽位為65536,這個(gè)ping消息的消息頭太大了,浪費(fèi)帶寬。
(2)redis的集群主節(jié)點(diǎn)數(shù)量基本不可能超過(guò)1000個(gè)。
如上所述,集群節(jié)點(diǎn)越多,心跳包的消息體內(nèi)攜帶的數(shù)據(jù)越多。如果節(jié)點(diǎn)過(guò)1000個(gè),也會(huì)導(dǎo)致網(wǎng)絡(luò)擁堵。因此redis作者,不建議redis
cluster節(jié)點(diǎn)數(shù)量超過(guò)1000個(gè)。
那么,對(duì)于節(jié)點(diǎn)數(shù)在1000以內(nèi)的redis cluster集群,16384個(gè)槽位夠用了。沒(méi)有必要拓展到65536個(gè)。
(3)槽位越小,節(jié)點(diǎn)少的情況下,壓縮比高
Redis主節(jié)點(diǎn)的配置信息中,它所負(fù)責(zé)的哈希槽是通過(guò)一張bitmap的形式來(lái)保存的,在傳輸過(guò)程中,會(huì)對(duì)bitmap進(jìn)行壓縮,但是如果bitmap的填充率slots
/ N很高的話(N表示節(jié)點(diǎn)數(shù)),bitmap的壓縮率就很低。
如果節(jié)點(diǎn)數(shù)很少,而哈希槽數(shù)量很多的話,bitmap的壓縮率就很低。
ps:文件壓縮率指的是,文件壓縮前后的大小比。
綜上所述,作者決定取16384個(gè)槽,不多不少,剛剛好!
總結(jié)
希望大家有所收獲!
熱門(mén)工具 換一換
