RedisCluster是redis的分佈式解決方案,在3.0版本後推出的方案,有效地解決了Redis分佈式的需求,當遇到單機內存、併發等瓶頸時,可使用此方案來解決這些問題
分佈式數據庫概念:
1,分佈式數據庫把整個數據按分區規則映射到多個節點,即把數據劃分到多個節點上,每個節點負責整體數據的一個子集,比如我們庫有900條用戶數據,有3個redis節點,將900條分成3份,分別存入到3個redis節點
2,分區規則:
常見的分區規則哈希分區和順序分區,redis集羣使用了哈希分區,順序分區暫用不到,不做具體說明;rediscluster採用了哈希分區的“虛擬槽分區”方式(哈希分區分節點取餘、一致性哈希分區和虛擬槽分區),其它兩種也不做介紹,有興趣可以百度瞭解一下。
3,虛擬槽分區(槽:slot)
RedisCluster採用此分區,所有的鍵根據哈希函數(CRC16[key]&16383)映射到0-16383槽內,共16384個槽位,每個節點維護部分槽及槽所映射的鍵值數據,哈希函數: Hash()=CRC16[key]&16383 按位與
槽與節點的關係如下
redis用虛擬槽分區原因:1,解耦數據與節點關係,節點自身維護槽映射關係,分佈式存儲
4,redisCluster的缺陷:
a,鍵的批量操作支持有限,比如mset, mget,如果多個鍵映射在不同的槽,就不支持了
b,鍵事務支持有限,當多個key分佈在不同節點時無法使用事務,同一節點是支持事務
c,鍵是數據分區的最小粒度,不能將一個很大的鍵值對映射到不同的節點
d,不支持多數據庫,只有0,select 0
e,複製結構只支持單層結構,不支持樹型結構。
集羣環境搭建-手動篇:
1,在/usr/local/bin/clusterconf目錄,
6389爲6379的從節點,6390爲6380的從節點,6391爲6381的從節點
2,分別修改6379、 6380、 7381、 6389、 6390、 6391配置文件
port 6379 //節點端口
cluster-enabled yes //開啓集羣模式
cluster-node-timeout 15000 //節點超時時間(接收pong消息回覆的時間)
cluster-config-file /usrlocalbin/cluster/data/nodes-6379.conf 集羣內部配置文件
其它節點的配置和這個一致,改端口即可
3,配置完後,啓動6個redis服務
命令:cd /usr/local/bin/clusterconf/data
cat nodes-6379.conf //查看6379節點ID值
也可以這樣查看 6379>cluster nodes
4,各節點啓動後,使用cluster meet ip port與各節點握手,是集羣通信的第一步
5,握手成功後,使用cluster nodes可以看到各節點都可以互相查詢到
6,節點握手成功後,此時集羣處理下線狀態,所有讀寫都被禁止
7,使用cluster info命令獲取集羣當前狀態
8,redis集羣有16384個哈希槽,要把所有數據映射到16384槽,需要批量設置槽
redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0...5461}
但我的虛擬機按範圍分配有問題,同學們回去試一下看有沒有問題
錯誤爲: (error) ERR Invalid or out of range slot
批量不行,單個是可以的
redis-cli -h 127.0.0.1 -p 6379 cluster addslots 1 2 3 4
因此,我寫一個腳本/usr/local/bin/addSlots.sh, 詳情可見些腳本……
執行這個腳本可分配好槽位……
9,分配完槽後,可查看集羣狀態
10,然後再查看cluster nodes,查看每個節點的ID
11,將6389,6390,6391與 6379,6380,6381做主從映射
127.0.0.1:6389> cluster replicate af2c99b58aa8a0a8fd82ba594654ce9264ffb9bc
127.0.0.1:6390> cluster replicate 2d6e6deb9512324336754b7b3fdf86032445c77c
127.0.0.1:6391> cluster replicate 61bd9fbbd3c154da318b502b86b1ee6516b82c17
12,注:這是手動模式,在生產環境我們一般採用以下自動模式安裝
自動安裝模式:
1,在/usr/local新建目錄:ruby
下載鏈接:https://pan.baidu.com/s/1kWsf3Rh 密碼:n3pc
從這個鏈接下載 ruby-2.3.1.tar.gz 和 redis-3.3.0.gem
解壓 tar -zxvf ruby-2.3.1.tar.gz
a, cd ruby-2.3.1
b, ./configure -prefix=/usr/local/ruby
c, make && make install //過程會有點慢,大概5-10分鐘
d, 然後gem install -l redis-3.3.0.gem //沒有gem需要安裝yum install gem
e,準備好6個節點,(注意不要設置requirepass),將/usr/local/bin/clusterconf/data的config-file刪除;依次啓動6個節點:./redis-server clusterconf/redis6379.conf
如果之前redis有數據存在,flushall清空;(坑:不需要cluster meet ..)
f, 進入cd /usr/local/bin, 執行以下:1代表從節點的個數
./redis-trib.rb create --replicas 1 192.168.1.111:6379 192.168.1.111:6380 192.168.1.111:6381 192.168.1.111:6389 192.168.1.111:6390 192.168.1.111:6391
主從分配,6379是6389的從節點
貌似只有主節點可讀寫,從節點不可以
主節點死後,從節點變成主節點
集羣健康檢測
redis-trib.rb check 192.168.1.111:6379 (注:redis先去註釋掉requirepass,不然連不上)
如此出現了這個問題,6379的5798槽位號被打開了
解決如下:
6379,6380,6381的有部分槽位被打開了,分別進入這幾個節點,執行
6380:>cluster setslot 1180 stable
cluster setslot 2998 stable
cluster setslot 11212 stable
其它也一樣,分別執行修復完後:
此時修復後的健康正常;
當停掉6379後,過會6389變成主節點
注意:使用客戶端工具查詢時要加-c
./redis-cli -h 192.168.1.111 -p 6379 -c
mset aa bb cc dd,批設置對應在不同的solt上,缺點
集羣正常啓動後,在每個redis.conf里加上
masterauth “12345678”
requiredpass “12345678”
當主節點下線時,從節點會變成主節點,用戶和密碼是很有必要的,設置成一致
這上面是一主一從,那能不能一主多從呢?
./redis-trib.rb create --replicas 2
192.168.1.111:6379 192.168.1.111:6380 192.168.1.111:6381
192.168.1.111:6479 192.168.1.111:6480 192.168.1.111:6481
192.168.1.111:6579 192.168.1.111:6580 192.168.1.111:6581
節點之間的通信
節點之間採用Gossip協議進行通信,Gossip協議就是指節點彼此之間不斷通信交換信息
當主從角色變化或新增節點,彼此通過ping/pong進行通信知道全部節點的最新狀態並達到集羣同步
Gossip協議
Gossip協議的主要職責就是信息交換,信息交換的載體就是節點之間彼此發送的Gossip消息,常用的Gossip消息有ping消息、pong消息、meet消息、fail消息
meet消息:用於通知新節點加入,消息發送者通知接收者加入到當前集羣,meet消息通信完後,接收節點會加入到集羣中,並進行週期性ping pong交換
ping消息:集羣內交換最頻繁的消息,集羣內每個節點每秒向其它節點發ping消息,用於檢測節點是在在線和狀態信息,ping消息發送封裝自身節點和其他節點的狀態數據;
pong消息,當接收到ping meet消息時,作爲響應消息返回給發送方,用來確認正常通信,pong消息也封閉了自身狀態數據;
fail消息:當節點判定集羣內的另一節點下線時,會向集羣內廣播一個fail消息,後面會講到。……
消息解析流程
所有消息格式爲:消息頭、消息體,消息頭包含發送節點自身狀態數據(比如節點ID、槽映射、節點角色、是否下線等),接收節點根據消息頭可以獲取到發送節點的相關數據。
選擇節點併發送ping消息:
Gossip協議信息的交換機制具有天然的分佈式特性,但ping pong發送的頻率很高,可以實時得到其它節點的狀態數據,但頻率高會加重帶寬和計算能力,因此每次都會有目的性地選擇一些節點; 但是節點選擇過少又會影響故障判斷的速度,redis集羣的Gossip協議兼顧了這兩者的優缺點,看下圖:
不難看出:節點選擇的流程可以看出消息交換成本主要體現在發送消息的節點數量和每個消息攜帶的數據量
流程說明:
- 選擇發送消息的節點數量:集羣內每個節點維護定時任務默認爲每秒執行10次,每秒會隨機選取5個節點,找出最久沒有通信的節點發送ping消息,用來保證信息交換的隨機性,每100毫秒都會掃描本地節點列表,如果發現節點最近一次接受pong消息的時間大於cluster-node-timeout/2 則立刻發送ping消息,這樣做目的是防止該節點信息太長時間沒更新,當我們寬帶資源緊張時,在可redis.conf將cluster-node-timeout 15000 改成30秒,但不能過度加大
- 消息數據:節點自身信息和其他節點信息
集羣擴容
這也是分佈式存儲最常見的需求,當我們存儲不夠用時,要考慮擴容
擴容步驟如下:
1.準備好新節點
2.加入集羣,遷移槽和數據
1),同目錄下新增redis6382.conf、redis6392.conf兩
啓動兩個新redis節點
./redis-server clusterconf/redis6382.conf & (新主節點)
./redis-server clusterconf/redis6392.conf & (新從節點)
2),新增主節點
./redis-trib.rb add-node 192.168.1.111:6382 192.168.1.111:6379
6379是原存在的主節點,6382是新的主節點
3),添加從節點
redis-trib.rb add-node --slave --master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2 192.168.1.111:6392 192.168.1.111:6379
--slave,表示添加的是從節點
--master-id 03ccad2ba5dd1e062464bc7590400441fafb63f2表示主節點6382的master_id
192.168.1.111:6392,新從節點
192.168.1.111:6379集羣原存在的舊節點
4),redis-trib.rb reshard 192.168.1.111:6382 //爲新主節點重新分配solt
How many slots do you want to move (from 1 to 16384)? 1000 //設置slot數1000
What is the receiving node ID? 464bc7590400441fafb63f2 //新節點node id
Source node #1:all //表示全部節點重新洗牌
新增完畢!
集羣減縮節點:
集羣同時也支持節點下線掉
下線的流程如下:
流程說明:
- 確定下線節點是否存在槽slot,如果有,需要先把槽遷移到其他節點,保證整個集羣槽節點映射的完整性;
- 當下線的節點沒有槽或本身是從節點時,就可以通知集羣內其它節點(或者叫忘記節點),當下線節點被忘記後正常關閉。
刪除節點也分兩種:
一種是主節點6382,一種是從節點6392。
在從節點6392中,沒有分配哈希槽,執行
./redis-trib.rb del-node 192.168.1.111:6392 7668541151b4c37d2d9 有兩個參數ip:port 和節點的id。 從節點6392從集羣中刪除了。
主節點6382刪除步驟:
1,./redis-trib.rb reshard 192.168.1.111:6382
問我們有多少個哈希槽要移走,因爲我們這個節點上剛分配了1000 個所以我們這裏輸入1000
2,最後
./redis-trib.rb del-node 192.168.1.111:6382 3e50c6398c75e0088a41f908071c2c2eda1dc900
此時節點下線完成……
請求路由重定向
我們知道,在redis集羣模式下,redis接收的任何鍵相關命令首先是計算這個鍵CRC值,通過CRC找到對應的槽位,再根據槽找到所對應的redis節點,如果該節點是本身,則直接處理鍵命令;如果不是,則回覆鍵重定向到其它節點,這個過程叫做MOVED重定向
故障轉移:
redis集羣實現了高可用,當集羣內少量節點出現故障時,通過故障轉移可以保證集羣正常對外提供服務。
當集羣裏某個節點出現了問題,redis集羣內的節點通過ping pong消息發現節點是否健康,是否有故障,其實主要環節也包括了 主觀下線和客觀下線;
主觀下線:指某個節點認爲另一個節點不可用,即下線狀態,當然這個狀態不是最終的故障判定,只能代表這個節點自身的意見,也有可能存在誤判;
下線流程:
- 節點a發送ping消息給節點b ,如果通信正常將接收到pong消息,節點a更新最近一次與節點b的通信時間;
- 如果節點a與節點b通信出現問題則斷開連接,下次會進行重連,如果一直通信失敗,則它們的最後通信時間將無法更新;
- 節點a內的定時任務檢測到與節點b最後通信時間超過cluster_note-timeout時,更新本地對節點b的狀態爲主觀下線(pfail)
客觀下線:指真正的下線,集羣內多個節點都認爲該節點不可用,達成共識,將它下線,如果下線的節點爲主節點,還要對它進行故障轉移
假如節點a標記節點b爲主觀下線,一段時間後節點a通過消息把節點b的狀態發到其它節點,當節點c接受到消息並解析出消息體時,會發現節點b的pfail狀態時,會觸發客觀下線流程;
當下線爲主節點時,此時redis集羣爲統計持有槽的主節點投票數是否達到一半,當下線報告統計數大於一半時,被標記爲客觀下線狀態。
故障恢復:
故障主節點下線後,如果下線節點的是主節點,則需要在它的從節點中選一個替換它,保證集羣的高可用;轉移過程如下:
- 資格檢查:檢查該從節點是否有資格替換故障主節點,如果此從節點與主節點斷開過通信,那麼當前從節點不具體故障轉移;
- 準備選舉時間:當從節點符合故障轉移資格後,更新觸發故障選舉時間,只有到達該時間後才能執行後續流程;
- 發起選舉:當到達故障選舉時間時,進行選舉;
- 選舉投票:只有持有槽的主節點纔有票,會處理故障選舉消息,投票過程其實是一個領導者選舉(選舉從節點爲領導者)的過程,每個主節點只能投一張票給從節點,
當從節點收集到足夠的選票(大於N/2+1)後,觸發替換主節點操作,撤銷原故障主節點的槽,委派給自己,並廣播自己的委派消息,通知集羣內所有節點。