當(dāng)前位置:首頁 > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]我們都知道Redis的集群有三種方案:1、主從復(fù)制模式2、Sentinel(哨兵)模式3、RedisCluster模式當(dāng)然使用隨著海量數(shù)據(jù)的存儲(chǔ)要求,單臺(tái)Redis配置有限,已經(jīng)滿足不了我們的需求。我們考慮采用分布式集群方案。RedisCluster采用數(shù)據(jù)分片機(jī)制,定義了163...


我們都知道Redis的集群有三種方案:

  • 1、主從復(fù)制模式
  • 2、Sentinel(哨兵)模式
  • 3、Redis Cluster模式
當(dāng)然使用隨著海量數(shù)據(jù)的存儲(chǔ)要求,單臺(tái)Redis配置有限,已經(jīng)滿足不了我們的需求。我們考慮采用分布式集群方案。

Redis Cluster 采用數(shù)據(jù)分片機(jī)制,定義了 16384個(gè) Slot槽位,集群中的每個(gè)Redis 實(shí)例負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。


客戶端可以連接集群中任意一個(gè)Redis 實(shí)例,發(fā)送讀寫命令,如果當(dāng)前Redis 實(shí)例收到不是自己負(fù)責(zé)的Slot的請(qǐng)求時(shí),會(huì)將該slot所在的正確的Redis 實(shí)例地址返回給客戶端。


客戶端收到后,自動(dòng)將原請(qǐng)求重新發(fā)到這個(gè)新地址,自動(dòng)操作,外部透明。

高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


是不是有點(diǎn)似曾相識(shí)的感覺,HTTP 協(xié)議也有重定向功能。玩法跟這個(gè)差不多。HTTP 響應(yīng)頭有一個(gè)Location字段,當(dāng)狀態(tài)碼是301或者302時(shí),客戶端會(huì)自動(dòng)讀取?Location中的新地址,自動(dòng)重定向發(fā)送請(qǐng)求。


Redis key的路由計(jì)算公式:slot ?= CRC16(key) % 16384

添加、刪除或者修改某一個(gè)節(jié)點(diǎn),都不會(huì)造成集群不可用的狀態(tài)。使用哈希槽的好處就在于可以方便的添加或移除節(jié)點(diǎn)。

當(dāng)需要增加節(jié)點(diǎn)時(shí),只需要把其他節(jié)點(diǎn)的某些哈希槽挪到新節(jié)點(diǎn);當(dāng)需要移除節(jié)點(diǎn)時(shí),只需要把移除節(jié)點(diǎn)上的哈希槽挪到其他節(jié)點(diǎn)。


高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


CRC16的算法原理:

  • 根據(jù)CRC16的標(biāo)準(zhǔn)選擇初值CRCIn的值
  • 將數(shù)據(jù)的第一個(gè)字節(jié)與CRCIn高8位異或
  • 判斷最高位,若該位為 0 左移一位,若為 1 左移一位再與多項(xiàng)式Hex碼異或
  • 重復(fù)3直至8位全部移位計(jì)算結(jié)束。
  • 重復(fù)將所有輸入數(shù)據(jù)操作完成以上步驟,所得16位數(shù)即16位CRC校驗(yàn)碼。

CRC16 算法最大值

CRC16 算法,產(chǎn)生的hash值有 16 bit 位,可以產(chǎn)生 65536(2^16)個(gè)值 ,也就是說值分布在 0 ~ 65535 之間


這時(shí)候,疑問來了,槽位總數(shù)為什么是 16384 ?65536 不可以嗎?

高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


這個(gè)問題,Redis 官方 Issues 也有朋友提出來過

地址:

https://github.com/redis/redis/issues/2576


高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


antirez 大神對(duì)這個(gè)問題做了回復(fù),簡(jiǎn)單歸納起來,有以下原因:

  • 正常的心跳數(shù)據(jù)包攜帶節(jié)點(diǎn)的完整配置,它能以冪等方式來更新配置。如果采用 16384 個(gè)插槽,占空間 2KB (16384/8);如果采用 65536 個(gè)插槽,占空間 8KB (65536/8)。

  • Redis Cluster 不太可能擴(kuò)展到超過 1000 個(gè)主節(jié)點(diǎn),太多可能導(dǎo)致網(wǎng)絡(luò)擁堵。

  • 16384 個(gè)插槽范圍比較合適,當(dāng)集群擴(kuò)展到1000個(gè)節(jié)點(diǎn)時(shí),也能確保每個(gè)master節(jié)點(diǎn)有足夠的插槽,

8KB 的心跳包看似不大,但是這個(gè)是心跳包每秒都要將本節(jié)點(diǎn)的信息同步給集群其他節(jié)點(diǎn)。比起 16384 個(gè)插槽,頭大小增加了4倍,ping消息的消息頭太大了,浪費(fèi)帶寬。

Redis主節(jié)點(diǎn)的哈希槽配置信息是通過 bitmap 來保存的


高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


傳輸過程中,會(huì)對(duì)bitmap進(jìn)行壓縮,bitmap的填充率越低,壓縮率越高。

bitmap 填充率 = slots / N (N表示節(jié)點(diǎn)數(shù)),

所以,插槽數(shù)偏低的話, 填充率會(huì)降低,壓縮率會(huì)升高。

綜合下來,從心跳包的大小、網(wǎng)絡(luò)帶寬、心跳并發(fā)、壓縮率等維度考慮,16384 個(gè)插槽更有優(yōu)勢(shì)且能滿足業(yè)務(wù)需求。

萬事萬物,都是相互制衡的,”大“ 不一定是最好的,合適最重要。


接下來,我們看下master節(jié)點(diǎn)間心跳數(shù)據(jù)包格式:

消息格式分為:消息頭和消息體。消息頭包含發(fā)送節(jié)點(diǎn)自身狀態(tài)數(shù)據(jù),接收節(jié)點(diǎn)根據(jù)消息頭就可以獲取到發(fā)送節(jié)點(diǎn)的相關(guān)數(shù)據(jù),

碼位置

/usr/src/redis/redis-5.0.7/src/cluster.h


高手過招,?為什么?Redis?Cluster?是16384個(gè)槽位?


其中,消息頭有一個(gè)myslots的char類型數(shù)組,unsigned char myslots[CLUSTER_SLOTS/8];,數(shù)組長(zhǎng)度為 16384/8 = 2048 。底層存儲(chǔ)其實(shí)是一個(gè)bitmap,每一個(gè)位代表一個(gè)槽,如果該位為1,表示這個(gè)槽是屬于這個(gè)節(jié)點(diǎn)。

消息體中,會(huì)攜帶一定數(shù)量的其他節(jié)點(diǎn)信息用于交換,約為集群總節(jié)點(diǎn)數(shù)量的1/10,節(jié)點(diǎn)數(shù)量越多,消息體內(nèi)容越大。10個(gè)節(jié)點(diǎn)的消息體大小約1kb。


劃重點(diǎn):

細(xì)心的同學(xué)可能會(huì)有疑問,char不是占2個(gè)字節(jié)嗎?數(shù)組長(zhǎng)度為什么是 16384/8?不應(yīng)該是 16384/16 嗎?


因?yàn)椋琑edis 是 C 語言開發(fā)的,char 占用一個(gè) 字節(jié);而 Java 語言 char 占用 兩個(gè) 字節(jié)。


master節(jié)點(diǎn)間心跳通訊

Redis 集群采用 Gossip(流言)協(xié)議, Gossip 協(xié)議工作原理就是節(jié)點(diǎn)彼此不斷通信交換信息,一段時(shí)間后所有的節(jié)點(diǎn)都會(huì)知道集群完整的信息,類似流言傳播。

集群中每個(gè)節(jié)點(diǎn)通過一定規(guī)則挑選要通信的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)可能知道全部節(jié)點(diǎn),也可能僅知道部分節(jié)點(diǎn),只要這些節(jié)點(diǎn)彼此可以正常通信,最終它們會(huì)達(dá)到一致的狀態(tài)。當(dāng)節(jié)點(diǎn)出現(xiàn)故障、新節(jié)點(diǎn)加入、主從角色變化、槽信息變更等事件發(fā)生時(shí),通過不斷的 ping/pong 消息通信,經(jīng)過一段時(shí)間后所有的節(jié)點(diǎn)都會(huì)知道整個(gè)集群 全部節(jié)點(diǎn)的最新狀態(tài),從而達(dá)到集群狀態(tài)同步的目的。

具體規(guī)則如下:

  • 1、每秒會(huì)隨機(jī)選取5個(gè)節(jié)點(diǎn),找出最久沒有通信的節(jié)點(diǎn)發(fā)送ping消息
  • 2、每隔 100毫秒 都會(huì)掃描本地節(jié)點(diǎn)列表,如果發(fā)現(xiàn)節(jié)點(diǎn)最近一次接受pong消息的時(shí)間大于cluster-node-timeout/2 ,則立刻發(fā)送ping消息
因此,每秒單master節(jié)點(diǎn)發(fā)出ping消息數(shù)量:

=?1? ?10?*?num(node.pong_received>cluster_node_timeout/2)
總結(jié):

1、每秒 redis節(jié)點(diǎn)需要發(fā)送一定數(shù)量的ping消息作為心跳包,如果槽位為 65536,這個(gè)ping消息的消息頭太大了,浪費(fèi)帶寬。

2、業(yè)務(wù)上看,集群主節(jié)點(diǎn)數(shù)量基本不可能超過1000個(gè)。集群節(jié)點(diǎn)越多,心跳包的消息體攜帶的數(shù)據(jù)越多。如果節(jié)點(diǎn)超過1000個(gè),會(huì)導(dǎo)致網(wǎng)絡(luò)擁堵。因此redis作者,不建議redis cluster節(jié)點(diǎn)數(shù)量超過1000個(gè)。

3、槽位越小,節(jié)點(diǎn)少的情況下,壓縮率更高

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉