Redis cluster的核心原理分析


一、節點間的內部通信機制

1、基礎通信原理

(1)redis cluster節點間采取gossip協議進行通信

跟集中式不同,不是將集群元數據(節點信息,故障,等等)集中存儲在某個節點上,而是互相之間不斷通信,保持整個集群所有節點的數據是完整的

維護集群的元數據用得,集中式,一種叫做gossip

集中式:好處在於,元數據的更新和讀取,時效性非常好,一旦元數據出現了變更,立即就更新到集中式的存儲中,其他節點讀取的時候立即就可以感知到;
不好在於,所有的元數據的跟新壓力全部集中在一個地方,可能會導致元數據的存儲有壓力

gossip:好處在於,元數據的更新比較分散,不是集中在一個地方,更新請求會陸陸續續,打到所有節點上去更新,有一定的延時,降低了壓力;
缺點,元數據更新有延時,可能導致集群的一些操作會有一些滯后

 我們剛才做reshard,去做另外一個操作,會發現說,configuration error,達成一致

(2)10000端口

每個節點都有一個專門用於節點間通信的端口,就是自己提供服務的端口號+10000,比如7001,那么用於節點間通信的就是17001端口

每隔節點每隔一段時間都會往另外幾個節點發送ping消息,同時其他幾點接收到ping之后返回pong

(3)交換的信息

故障信息,節點的增加和移除,hash slot信息,等等

2、gossip協議

gossip協議包含多種消息,包括ping,pong,meet,fail,等等

meet: 某個節點發送meet給新加入的節點,讓新節點加入集群中,然后新節點就會開始與其他節點進行通信

redis-trib.rb add-node

其實內部就是發送了一個gossip meet消息,給新加入的節點,通知那個節點去加入我們的集群

ping: 每個節點都會頻繁給其他節點發送ping,其中包含自己的狀態還有自己維護的集群元數據,互相通過ping交換元數據

每個節點每秒都會頻繁發送ping給其他的集群,ping,頻繁的互相之間交換數據,互相進行元數據的更新

pong: 返回ping和meet,包含自己的狀態和其他信息,也可以用於信息廣播和更新

fail: 某個節點判斷另一個節點fail之后,就發送fail給其他節點,通知其他節點,指定的節點宕機了

3、ping消息深入

ping很頻繁,而且要攜帶一些元數據,所以可能會加重網絡負擔

每個節點每秒會執行10次ping,每次會選擇5個最久沒有通信的其他節點

當然如果發現某個節點通信延時達到了cluster_node_timeout / 2,那么立即發送ping,避免數據交換延時過長,落后的時間太長了

比如說,兩個節點之間都10分鍾沒有交換數據了,那么整個集群處於嚴重的元數據不一致的情況,就會有問題

所以cluster_node_timeout可以調節,如果調節比較大,那么會降低發送的頻率

每次ping,一個是帶上自己節點的信息,還有就是帶上1/10其他節點的信息,發送出去,進行數據交換

至少包含3個其他節點的信息,最多包含總節點-2個其他節點的信息

 

二、面向集群的jedis內部實現原理

開發,jedis,redis的java client客戶端,redis cluster,jedis cluster api

jedis cluster api與redis cluster集群交互的一些基本原理

1、基於重定向的客戶端

redis-cli -c,自動重定向

(1)請求重定向

客戶端可能會挑選任意一個redis實例去發送命令,每個redis實例接收到命令,都會計算key對應的hash slot

如果在本地就在本地處理,否則返回moved給客戶端,讓客戶端進行重定向

cluster keyslot mykey,可以查看一個key對應的hash slot是什么

用redis-cli的時候,可以加入-c參數,支持自動的請求重定向,redis-cli接收到moved之后,會自動重定向到對應的節點執行命令

(2)計算hash slot

計算hash slot的算法,就是根據key計算CRC16值,然后對16384取模,拿到對應的hash slot

用hash tag可以手動指定key對應的slot,同一個hash tag下的key,都會在一個hash slot中,比如set mykey1:{100}和set mykey2:{100}

(3)hash slot查找

節點間通過gossip協議進行數據交換,就知道每個hash slot在哪個節點上

2、smart jedis

(1)什么是smart jedis

基於重定向的客戶端,很消耗網絡IO,因為大部分情況下,可能都會出現一次請求重定向,才能找到正確的節點

所以大部分的客戶端,比如java redis客戶端,就是jedis,都是smart的

本地維護一份hashslot -> node的映射表,緩存,大部分情況下,直接走本地緩存就可以找到hashslot -> node,不需要通過節點進行moved重定向

(2)JedisCluster的工作原理

在JedisCluster初始化的時候,就會隨機選擇一個node,初始化hashslot -> node映射表,同時為每個節點創建一個JedisPool連接池

每次基於JedisCluster執行操作,首先JedisCluster都會在本地計算key的hashslot,然后在本地映射表找到對應的節點

如果那個node正好還是持有那個hashslot,那么就ok; 如果說進行了reshard這樣的操作,可能hashslot已經不在那個node上了,就會返回moved

如果JedisCluter API發現對應的節點返回moved,那么利用該節點的元數據,更新本地的hashslot -> node映射表緩存

重復上面幾個步驟,直到找到對應的節點,如果重試超過5次,那么就報錯,JedisClusterMaxRedirectionException

jedis老版本,可能會出現在集群某個節點故障還沒完成自動切換恢復時,頻繁更新hash slot,頻繁ping節點檢查活躍,導致大量網絡IO開銷

jedis最新版本,對於這些過度的hash slot更新和ping,都進行了優化,避免了類似問題

(3)hashslot遷移和ask重定向

如果hash slot正在遷移,那么會返回ask重定向給jedis

jedis接收到ask重定向之后,會重新定位到目標節點去執行,但是因為ask發生在hash slot遷移過程中,所以JedisCluster API收到ask是不會更新hashslot本地緩存

已經可以確定說,hashslot已經遷移完了,moved是會更新本地hashslot->node映射表緩存的

三、高可用性與主備切換原理

redis cluster的高可用的原理,幾乎跟哨兵是類似的

1、判斷節點宕機

如果一個節點認為另外一個節點宕機,那么就是pfail,主觀宕機

如果多個節點都認為另外一個節點宕機了,那么就是fail,客觀宕機,跟哨兵的原理幾乎一樣,sdown,odown

在cluster-node-timeout內,某個節點一直沒有返回pong,那么就被認為pfail

如果一個節點認為某個節點pfail了,那么會在gossip ping消息中,ping給其他節點,如果超過半數的節點都認為pfail了,那么就會變成fail

2、從節點過濾

對宕機的master node,從其所有的slave node中,選擇一個切換成master node

檢查每個slave node與master node斷開連接的時間,如果超過了cluster-node-timeout * cluster-slave-validity-factor,那么就沒有資格切換成master

這個也是跟哨兵是一樣的,從節點超時過濾的步驟

3、從節點選舉

哨兵:對所有從節點進行排序,slave priority,offset,run id

每個從節點,都根據自己對master復制數據的offset,來設置一個選舉時間,offset越大(復制數據越多)的從節點,選舉時間越靠前,優先進行選舉

所有的master node開始slave選舉投票,給要進行選舉的slave進行投票,如果大部分master node(N/2 + 1)都投票給了某個從節點,那么選舉通過,那個從節點可以切換成master

從節點執行主備切換,從節點切換為主節點

4、與哨兵比較

整個流程跟哨兵相比,非常類似,所以說,redis cluster功能強大,直接集成了replication和sentinal的功能

 

1、fork耗時導致高並發請求延時

RDB和AOF的時候,其實會有生成RDB快照,AOF rewrite,耗費磁盤IO的過程,主進程fork子進程

fork的時候,子進程是需要拷貝父進程的空間內存頁表的,也是會耗費一定的時間的

一般來說,如果父進程內存有1個G的數據,那么fork可能會耗費在20ms左右,如果是10G~30G,那么就會耗費20 * 10,甚至20 * 30,也就是幾百毫秒的時間

info stats中的latest_fork_usec,可以看到最近一次form的時長

redis單機QPS一般在幾萬,fork可能一下子就會拖慢幾萬條操作的請求時長,從幾毫秒變成1秒

優化思路

fork耗時跟redis主進程的內存有關系,一般控制redis的內存在10GB以內,slave -> master,全量復制

2、AOF的阻塞問題

redis將數據寫入AOF緩沖區,單獨開一個現場做fsync操作,每秒一次

但是redis主線程會檢查兩次fsync的時間,如果距離上次fsync時間超過了2秒,那么寫請求就會阻塞

everysec,最多丟失2秒的數據

一旦fsync超過2秒的延時,整個redis就被拖慢

優化思路

優化硬盤寫入速度,建議采用SSD,不要用普通的機械硬盤,SSD,大幅度提升磁盤讀寫的速度

3、主從復制延遲問題

主從復制可能會超時嚴重,這個時候需要良好的監控和報警機制

在info replication中,可以看到master和slave復制的offset,做一個差值就可以看到對應的延遲量

如果延遲過多,那么就進行報警

4、主從復制風暴問題

如果一下子讓多個slave從master去執行全量復制,一份大的rdb同時發送到多個slave,會導致網絡帶寬被嚴重占用

如果一個master真的要掛載多個slave,那盡量用樹狀結構,不要用星型結構

5、vm.overcommit_memory

0: 檢查有沒有足夠內存,沒有的話申請內存失敗
1: 允許使用內存直到用完為止
2: 內存地址空間不能超過swap + 50%

如果是0的話,可能導致類似fork等操作執行失敗,申請不到足夠的內存空間

cat /proc/sys/vm/overcommit_memory
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1

6、swapiness

cat /proc/version,查看linux內核版本

如果linux內核版本<3.5,那么swapiness設置為0,這樣系統寧願swap也不會oom killer(殺掉進程)
如果linux內核版本>=3.5,那么swapiness設置為1,這樣系統寧願swap也不會oom killer

保證redis不會被殺掉

echo 0 > /proc/sys/vm/swappiness
echo vm.swapiness=0 >> /etc/sysctl.conf

7、最大打開文件句柄

ulimit -n 10032 10032

自己去上網搜一下,不同的操作系統,版本,設置的方式都不太一樣

8、tcp backlog

cat /proc/sys/net/core/somaxconn
echo 511 > /proc/sys/net/core/somaxconn

 

redis:持久化、復制(主從架構)、哨兵(高可用,主備切換)、redis cluster(海量數據+橫向擴容+高可用/主備切換)

持久化:高可用的一部分,在發生redis集群災難的情況下(比如說部分master+slave全部死掉了),如何快速進行數據恢復,快速實現服務可用,才能實現整個系統的高可用

復制:主從架構,master -> slave 復制,讀寫分離的架構,寫master,讀slave,橫向擴容slave支撐更高的讀吞吐,讀高並發,10萬,20萬,30萬,上百萬,QPS,橫向擴容

哨兵:高可用,主從架構,在master故障的時候,快速將slave切換成master,實現快速的災難恢復,實現高可用性

redis cluster:多master讀寫,數據分布式的存儲,橫向擴容,水平擴容,快速支撐高達的數據量+更高的讀寫QPS,自動進行master -> slave的主備切換,高可用

讓底層的緩存系統,redis,實現能夠任意水平擴容,支撐海量數據(1T+,幾十T,10G * 600 redis = 6T),支撐很高的讀寫QPS(redis單機在幾萬QPS,10台,幾十萬QPS),高可用性(給我們每個redis實例都做好AOF+RDB的備份策略+容災策略,slave -> master主備切換)

1T+海量數據、10萬+讀寫QPS、99.99%高可用性

3、redis的第一套企業級的架構

如果你的數據量不大,單master就可以容納,一般來說你的緩存的總量在10G以內就可以,那么建議按照以下架構去部署redis

redis持久化+備份方案+容災方案+replication(主從+讀寫分離)+sentinal(哨兵集群,3個節點,高可用性)

可以支撐的數據量在10G以內,可以支撐的寫QPS在幾萬左右,可以支撐的讀QPS可以上10萬以上(隨你的需求,水平擴容slave節點就可以),可用性在99.99%

4、redis的第二套企業級架構

多master分布式存儲數據,水平擴容

支撐更多的數據量,1T+以上沒問題,只要擴容master即可

讀寫QPS分別都達到幾十萬都沒問題,只要擴容master即可,redis cluster,讀寫分離,支持不太好,readonly才能去slave上讀

支撐99.99%可用性,也沒問題,slave -> master的主備切換,冗余slave去進一步提升可用性的方案(每個master掛一個slave,但是整個集群再加個3個slave冗余一下)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM