redis cluster
redis cluster是Redis的分布式解決方案,在3.0版本推出后有效地解決了redis分布式方面的需求
自動將數(shù)據(jù)進行分片,每個master上放一部分數(shù)據(jù)
提供內(nèi)置的高可用支持,部分master不可用時,還是可以繼續(xù)工作的
支撐N個redis master node,每個master node都可以掛載多個slave node
高可用,因為每個master都有salve節(jié)點,那么如果mater掛掉,redis cluster這套機制,就會自動將某個slave切換成master

redis cluster vs. replication + sentinal
如果你的數(shù)據(jù)量很少,主要是承載高并發(fā)高性能的場景,比如你的緩存一般就幾個G,單機足夠了
replication,一個mater,多個slave,要幾個slave跟你的要求的讀吞吐量有關(guān)系,然后自己搭建一個sentinal集群,去保證redis主從架構(gòu)的高可用性,就可以了
redis cluster,主要是針對海量數(shù)據(jù)+高并發(fā)+高可用的場景,海量數(shù)據(jù),如果你的數(shù)據(jù)量很大,那么建議就用redis cluster
數(shù)據(jù)分布算法
hash算法
比如你有 N 個 redis實例,那么如何將一個key映射到redis上呢,你很可能會采用類似下面的通用方法計算 key的 hash 值,然后均勻的映射到到 N 個 redis上:
hash(key)%N
如果增加一個redis,映射公式變成了 hash(key)%(N+1)
如果一個redis宕機了,映射公式變成了 hash(key)%(N-1)
在這兩種情況下,幾乎所有的緩存都失效了。會導(dǎo)致數(shù)據(jù)庫訪問的壓力陡增,嚴重情況,還可能導(dǎo)致數(shù)據(jù)庫宕機。
一致性hash算法

一個master宕機不會導(dǎo)致大部分緩存失效,可能存在緩存熱點問題
用虛擬節(jié)點改進

redis cluster的hash slot算法
redis cluster有固定的16384個hash slot,對每個key計算CRC16值,然后對16384取模,可以獲取key對應(yīng)的hash slot
redis cluster中每個master都會持有部分slot,比如有3個master,那么可能每個master持有5000多個hash slot
hash slot讓node的增加和移除很簡單,增加一個master,就將其他master的hash slot移動部分過去,減少一個master,就將它的hash slot移動到其他master上去
移動hash slot的成本是非常低的
客戶端的api,可以對指定的數(shù)據(jù),讓他們走同一個hash slot,通過hash tag來實現(xiàn)
127.0.0.1:7000>CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000 可以將槽0-5000指派給節(jié)點7000負責。
每個節(jié)點都會記錄哪些槽指派給了自己,哪些槽指派給了其他節(jié)點。
客戶端向節(jié)點發(fā)送鍵命令,節(jié)點要計算這個鍵屬于哪個槽。
如果是自己負責這個槽,那么直接執(zhí)行命令,如果不是,向客戶端返回一個MOVED錯誤,指引客戶端轉(zhuǎn)向正確的節(jié)點。
redis cluster 多master的寫入
在redis cluster寫入數(shù)據(jù)的時候,其實是你可以將請求發(fā)送到任意一個master上去執(zhí)行
但是,每個master都會計算這個key對應(yīng)的CRC16值,然后對16384個hashslot取模,找到key對應(yīng)的hashslot,找到hashslot對應(yīng)的master
如果對應(yīng)的master就在自己本地的話,set mykey1 v1,mykey1這個key對應(yīng)的hashslot就在自己本地,那么自己就處理掉了
但是如果計算出來的hashslot在其他master上,那么就會給客戶端返回一個moved error,告訴你,你得到哪個master上去執(zhí)行這條寫入的命令
什么叫做多master的寫入,就是每條數(shù)據(jù)只能存在于一個master上,不同的master負責存儲不同的數(shù)據(jù),分布式的數(shù)據(jù)存儲
100w條數(shù)據(jù),5個master,每個master就負責存儲20w條數(shù)據(jù),分布式數(shù)據(jù)存儲
默認情況下,redis cluster的核心的理念,主要是用slave做高可用的,每個master掛一兩個slave,主要是做數(shù)據(jù)的熱備,還有master故障時的主備切換,實現(xiàn)高可用的
redis cluster默認是不支持slave節(jié)點讀或者寫的,跟我們手動基于replication搭建的主從架構(gòu)不一樣的
jedis客戶端,對redis cluster的讀寫分離支持不太好的
默認的話就是讀和寫都到master上去執(zhí)行的
如果你要讓最流行的jedis做redis cluster的讀寫分離的訪問,那可能還得自己修改一點jedis的源碼,成本比較高
讀寫分離,是為了什么,主要是因為要建立一主多從的架構(gòu),才能橫向任意擴展slave node去支撐更大的讀吞吐量
redis cluster的架構(gòu)下,實際上本身master就是可以任意擴展的,你如果要支撐更大的讀吞吐量,或者寫吞吐量,或者數(shù)據(jù)量,都可以直接對master進行橫向擴展就可以了
節(jié)點間的內(nèi)部通信機制
1、基礎(chǔ)通信原理
(1)redis cluster節(jié)點間采取gossip協(xié)議進行通信
跟集中式不同,不是將集群元數(shù)據(jù)(節(jié)點信息,故障,等等)集中存儲在某個節(jié)點上,而是互相之間不斷通信,保持整個集群所有節(jié)點的數(shù)據(jù)是完整的
集中式:好處在于,元數(shù)據(jù)的更新和讀取,時效性非常好,一旦元數(shù)據(jù)出現(xiàn)了變更,立即就更新到集中式的存儲中,其他節(jié)點讀取的時候立即就可以感知到; 不好在于,所有的元數(shù)據(jù)的跟新壓力全部集中在一個地方,可能會導(dǎo)致元數(shù)據(jù)的存儲有壓力
gossip:好處在于,元數(shù)據(jù)的更新比較分散,不是集中在一個地方,更新請求會陸陸續(xù)續(xù),打到所有節(jié)點上去更新,有一定的延時,降低了壓力; 缺點,元數(shù)據(jù)更新有延時,可能導(dǎo)致集群的一些操作會有一些滯后
(2)10000端口
每個節(jié)點都有一個專門用于節(jié)點間通信的端口,就是自己提供服務(wù)的端口號+10000,比如7001,那么用于節(jié)點間通信的就是17001端口
每隔節(jié)點每隔一段時間都會往另外幾個節(jié)點發(fā)送ping消息,同時其他幾點接收到ping之后返回pong
(3)交換的信息
故障信息,節(jié)點的增加和移除,hash slot信息,等等
2、gossip協(xié)議
gossip協(xié)議包含多種消息,包括ping,pong,meet,fail,等等
meet: 某個節(jié)點發(fā)送meet給新加入的節(jié)點,讓新節(jié)點加入集群中,然后新節(jié)點就會開始與其他節(jié)點進行通信
redis-trib.rb add-node
其實內(nèi)部就是發(fā)送了一個gossip meet消息,給新加入的節(jié)點,通知那個節(jié)點去加入我們的集群
ping: 每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元數(shù)據(jù)
每個節(jié)點每秒都會頻繁發(fā)送ping給其他的集群,ping,頻繁的互相之間交換數(shù)據(jù),互相進行元數(shù)據(jù)的更新
pong: 返回ping和meet,包含自己的狀態(tài)和其他信息,也可以用于信息廣播和更新
fail: 某個節(jié)點判斷另一個節(jié)點fail之后,就發(fā)送fail給其他節(jié)點,通知其他節(jié)點,指定的節(jié)點宕機了
3、ping消息深入
ping很頻繁,而且要攜帶一些元數(shù)據(jù),所以可能會加重網(wǎng)絡(luò)負擔
每個節(jié)點每秒會執(zhí)行10次ping,每次會選擇5個最久沒有通信的其他節(jié)點
當然如果發(fā)現(xiàn)某個節(jié)點通信延時達到了cluster_node_timeout / 2,那么立即發(fā)送ping,避免數(shù)據(jù)交換延時過長,落后的時間太長了
比如說,兩個節(jié)點之間都10分鐘沒有交換數(shù)據(jù)了,那么整個集群處于嚴重的元數(shù)據(jù)不一致的情況,就會有問題
所以cluster_node_timeout可以調(diào)節(jié),如果調(diào)節(jié)比較大,那么會降低發(fā)送的頻率
每次ping,一個是帶上自己節(jié)點的信息,還有就是帶上1/10其他節(jié)點的信息,發(fā)送出去,進行數(shù)據(jù)交換
至少包含3個其他節(jié)點的信息,最多包含總節(jié)點-2個其他節(jié)點的信息
基于重定向的客戶端
(1)請求重定向
客戶端可能會挑選任意一個redis實例去發(fā)送命令,每個redis實例接收到命令,都會計算key對應(yīng)的hash slot
如果在本地就在本地處理,否則返回moved給客戶端,讓客戶端進行重定向
cluster keyslot mykey,可以查看一個key對應(yīng)的hash slot是什么
用redis-cli的時候,可以加入-c參數(shù),支持自動的請求重定向,redis-cli接收到moved之后,會自動重定向到對應(yīng)的節(jié)點執(zhí)行命令
(2)計算hash slot
計算hash slot的算法,就是根據(jù)key計算CRC16值,然后對16384取模,拿到對應(yīng)的hash slot
用hash tag可以手動指定key對應(yīng)的slot,同一個hash tag下的key,都會在一個hash slot中,比如set mykey1:{100}和set mykey2:{100}
(3)hash slot查找
節(jié)點間通過gossip協(xié)議進行數(shù)據(jù)交換,就知道每個hash slot在哪個節(jié)點上
smart jedis
(1)什么是smart jedis
基于重定向的客戶端,很消耗網(wǎng)絡(luò)IO,因為大部分情況下,可能都會出現(xiàn)一次請求重定向,才能找到正確的節(jié)點
所以大部分的客戶端,比如java redis客戶端,就是jedis,都是smart的
本地維護一份hashslot -> node的映射表,緩存,大部分情況下,直接走本地緩存就可以找到hashslot -> node,不需要通過節(jié)點進行moved重定向
(2)JedisCluster的工作原理
在JedisCluster初始化的時候,就會隨機選擇一個node,初始化hashslot -> node映射表,同時為每個節(jié)點創(chuàng)建一個JedisPool連接池
每次基于JedisCluster執(zhí)行操作,首先JedisCluster都會在本地計算key的hashslot,然后在本地映射表找到對應(yīng)的節(jié)點
如果那個node正好還是持有那個hashslot,那么就ok; 如果說進行了reshard這樣的操作,可能hashslot已經(jīng)不在那個node上了,就會返回moved
如果JedisCluter API發(fā)現(xiàn)對應(yīng)的節(jié)點返回moved,那么利用該節(jié)點的元數(shù)據(jù),更新本地的hashslot -> node映射表緩存
重復(fù)上面幾個步驟,直到找到對應(yīng)的節(jié)點,如果重試超過5次,那么就報錯,JedisClusterMaxRedirectionException
jedis老版本,可能會出現(xiàn)在集群某個節(jié)點故障還沒完成自動切換恢復(fù)時,頻繁更新hash slot,頻繁ping節(jié)點檢查活躍,導(dǎo)致大量網(wǎng)絡(luò)IO開銷
jedis最新版本,對于這些過度的hash slot更新和ping,都進行了優(yōu)化,避免了類似問題
(3)hashslot遷移和ask重定向
如果hash slot正在遷移,那么會返回ask重定向給jedis
jedis接收到ask重定向之后,會重新定位到目標節(jié)點去執(zhí)行,但是因為ask發(fā)生在hash slot遷移過程中,所以JedisCluster API收到ask是不會更新hashslot本地緩存
已經(jīng)可以確定說,hashslot已經(jīng)遷移完了,moved是會更新本地hashslot->node映射表緩存的
高可用性與主備切換原理
redis cluster的高可用的原理,幾乎跟哨兵是類似的
1、判斷節(jié)點宕機
如果一個節(jié)點認為另外一個節(jié)點宕機,那么就是pfail,主觀宕機
如果多個節(jié)點都認為另外一個節(jié)點宕機了,那么就是fail,客觀宕機,跟哨兵的原理幾乎一樣,sdown,odown
在cluster-node-timeout內(nèi),某個節(jié)點一直沒有返回pong,那么就被認為pfail
如果一個節(jié)點認為某個節(jié)點pfail了,那么會在gossip ping消息中,ping給其他節(jié)點,如果超過半數(shù)的節(jié)點都認為pfail了,那么就會變成fail
2、從節(jié)點過濾
對宕機的master node,從其所有的slave node中,選擇一個切換成master node
檢查每個slave node與master node斷開連接的時間,如果超過了cluster-node-timeout * cluster-slave-validity-factor,那么就沒有資格切換成master
這個也是跟哨兵是一樣的,從節(jié)點超時過濾的步驟
3、從節(jié)點選舉
哨兵:對所有從節(jié)點進行排序,slave priority,offset,run id
每個從節(jié)點,都根據(jù)自己對master復(fù)制數(shù)據(jù)的offset,來設(shè)置一個選舉時間,offset越大(復(fù)制數(shù)據(jù)越多)的從節(jié)點,選舉時間越靠前,優(yōu)先進行選舉
所有的master node開始slave選舉投票,給要進行選舉的slave進行投票,如果大部分master node(N/2 + 1)都投票給了某個從節(jié)點,那么選舉通過,那個從節(jié)點可以切換成master
從節(jié)點執(zhí)行主備切換,從節(jié)點切換為主節(jié)點
4、與哨兵比較
整個流程跟哨兵相比,非常類似,所以說,redis cluster功能強大,直接集成了replication和sentinal的功能
補充:redis集群(cluster)模式 詳解與實操
一 概念
Redis集群是一個分布式(distributed)、容錯(fault-tolerant)的 Redis內(nèi)存K/V服務(wù), 集群可以使用的功能是普通單機 Redis 所能使用的功能的一個子集(subset),比如Redis集群并不支持處理多個keys的命令,因為這需要在不同的節(jié)點間移動數(shù)據(jù),從而達不到像Redis那樣的性能,在高負載的情況下可能會導(dǎo)致不可預(yù)料的錯誤。
二 特性
Redis集群的幾個重要特征:
1.Redis 集群的分片特征在于將鍵空間分拆了16384個槽位,每一個節(jié)點負責其中一些槽位。
2.Redis提供一定程度的可用性,可以在某個節(jié)點宕機或者不可達的情況下繼續(xù)處理命令.(只要集群中大多數(shù)Master可達、且失效的Master至少有一個Slave可達,即集群非Fail狀態(tài),集群都是可用)
3.Redis 集群中不存在中心(central)節(jié)點或者代理(proxy)節(jié)點, 集群的其中一個主要設(shè)計目標是達到線性可擴展性(linear scalability)
三 架構(gòu)
3.1 redis-cluster架構(gòu)圖

實現(xiàn)哈希映射

架構(gòu)細節(jié):
所有的redis節(jié)點彼此互聯(lián)(PING-PONG機制),內(nèi)部使用二進制協(xié)議優(yōu)化傳輸速度和帶寬.
節(jié)點的fail是通過集群中超過半數(shù)的節(jié)點檢測失效時才生效.
客戶端與redis節(jié)點直連,不需要中間proxy層.客戶端不需要連接集群所有節(jié)點,連接集群中任何一個可用節(jié)點即可
redis-cluster把所有的物理節(jié)點映射到[0-16383]slot上,cluster 負責維護node->slot->value
3.2 redis-cluster選舉:容錯

選舉過程是集群中所有master參與,如果半數(shù)以上master節(jié)點與某個master節(jié)點通信超時(cluster-node-timeout),認為該master節(jié)點掛掉.
什么時候整個集群不可用(cluster_state:fail):
a:如果集群任意master掛掉,且當前master沒有slave,集群進入fail狀態(tài),也可以理解成集群的slot映射[0-16383]不完全時進入fail狀態(tài).
b:如果集群超過半數(shù)以上master掛掉,無論是否有slave,集群進入fail狀態(tài).
當集群不可用時,所有對集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤。
四 環(huán)境要求
Redis3.0版本以上
要讓集群正常工作至少需要3個主節(jié)點,在這里我們要創(chuàng)建6個redis節(jié)點,其中三個為主節(jié)點,三個為從節(jié)點
五 集群搭建
5.1 安裝ruby
apt-get install ruby
apt-get install rubygems
安裝ruby redis 擴展包:
5.2. 安裝redis server:
apt-get install redis-server
5.3. 創(chuàng)建redis-cluster文件夾
mkdir /usr/local/redis-cluster
5.4. 將你的redis.conf配置文件分別復(fù)制到redis-cluster這個文件夾中
更改如下
port 6379 #端口6379
bind 本機ip #默認ip為172.20.0.2/3/4/5/6/7需要改為其他節(jié)點機器可訪問的ip 否則創(chuàng)建集群時無法訪問對應(yīng)的端口,無法創(chuàng)建集群
daemonize yes #redis后臺運行
pidfile /var/run/redis_6379.pid #pidfile #文件對應(yīng)6379
cluster-enabled yes #開啟集群 把注釋#去掉
cluster-config-file nodes_7000.conf #集群的配置 配置文件首次啟動自動生成
cluster-node-timeout 15000 #請求超時 默認15秒,可自行設(shè)置
appendonly yes #aof日志開啟 有需要就開啟,它會每次寫操作都記錄一條日志
masterauth '86a1b907d54bf7010394bf316e183e67'
requirepass '86a1b907d54bf7010394bf316e183e67' #設(shè)置密碼
5.5. 啟動redis服務(wù)
需要啟動6個服務(wù)
redis-server /usr/local/redis_cluster/redis.conf
5.6 創(chuàng)建集群
其中redis-trib.rb在你的redis安裝目錄/src下:
/usr/local/redis-cluster/redis-trib.rb create --replicas 1 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379
可以看到創(chuàng)建集群成功了:(如圖:2,3,4為主,5,6,7為從)

5.7 設(shè)置redis集群密碼
如下圖,逐一設(shè)置redis密碼:

5.8 連接集群并測試

六 集群擴容
6.1 添加主節(jié)
創(chuàng)建一個新節(jié)點(使用的配置文件與之前一樣)并啟動。
執(zhí)行命令./redis-trib.rb add-node 172.20.0.8:6379 172.20.0.7:6379,將新節(jié)點添加到集群里

(腳本先檢查了集群狀態(tài),后添加了節(jié)點,添加后節(jié)點是空節(jié)點,還需分配哈希槽,沒有哈希槽就沒有儲存功能)
6.2 為新節(jié)點分配哈希槽
./redis-trib.rb reshard 172.20.0.2:6379

注釋:
want to move (from 1 to 16384)? 1000(此處為想移動多少個哈希槽)
What is the receiving node ID?(為加入的新節(jié)點的id)
Source node #1:all(此處選擇all,全部使用并移動,表示從所有主節(jié)點中隨機轉(zhuǎn)移,湊夠1000個哈希槽
6.3 添加從節(jié)點slave
如上新建一個空節(jié)點并啟動
./redis-trib.rb add-node 172.20.0.9:6379 172.20.0.7:6379

連接上新節(jié)點,執(zhí)行命令cluster replicate (任意一個master的ID)。
查看當前節(jié)點狀態(tài):

執(zhí)行命令cluster replicate (任意一個master的ID):

七 刪除節(jié)點
7.1 遷移slots
把節(jié)點擁有的slots全部遷移出去
./redis-trib.rb reshard 172.20.0.2:6379

7.2 刪除節(jié)點
redis-trib.rb del-node 172.20.0.2:6379 f113752f7763ceea613cb73018c043f8849edecf

7.3 沒有slots
如果沒有一個slots,可直接刪除
redis-trib.rb del-node 172.20.0.2:6379 9f5bc99dba963671bb5664f84eda304dc94b1fcb

(這個主節(jié)點被刪除之后,它之前擁有的從節(jié)點會自動成為其他主節(jié)點的從節(jié)點.)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- k8s部署redis cluster集群的實現(xiàn)
- Redis6.0搭建集群Redis-cluster的方法
- Redis Cluster集群主從切換的踩坑與填坑
- Redis Cluster集群數(shù)據(jù)分片機制原理
- docker redis5.0 cluster集群搭建的實現(xiàn)
- 使用Ruby腳本部署Redis Cluster集群步驟講解
- php成功操作redis cluster集群的實例教程
- Redis cluster集群的介紹
- Redis的Cluster集群搭建的實現(xiàn)步驟