redis集群簡述
哨兵模式中如果主從中master宕機了,是通過哨兵來選舉出新的master,在這個選舉切換主從的過程,整個redis服務是不可用的。而且哨兵模式中只有一個主節點對外提供服務,因此沒法支持更高的並發。而且當個主節點的內存設置也不宜過大。否則會導致持久化文件過大,影響數據恢復或主從同步的效率。
redis集群是由一系列的主從節點群組成的分布式服務器群,它具有復制、高可用和分片特性。Redis集群不需要 sentinel哨兵也能完成節點移除和故障轉移的功能。需要將每個節點設置成集群模式,這種集群模式沒有中心節點,客戶端通過CRC16算法對key進行hash
得到一個值,來判斷該key存儲在哪個主從服務上面,因此就算是某一個主從整個宕機,redis集群也是部分可用的。方便水平擴展,可以根據業務規模可以隨時加減配置。據官方文檔稱可以線性擴展到上萬個節點(但是官方推薦不超過1000個節點)。redis集群的性能和高可用性均優於哨兵模式。
Redis集群搭建
1.修改redis.conf配置文件
- daemonize yes 后台啟動
- cluster-enabled yes 開啟集群模式
- cluster-config-file nodes-6379.conf 集群配置信息存放文件名
- cluster-node-timeout 5000 節點離線時間限制,到達此值時發起某個主從重新選舉master
- protected-mode no 關閉保護模式
- requirepass xxx 設置本機密碼
- masterauth xxx 設置訪問別的機器的密碼
2.注意關閉服務器的防火牆,否則可能造成節點之間無法通信,無法搭建集群
使用修改好的配置文件啟動redis服務,我這里使用三個一主一從來搭建。因此先將6個redis服務使用指定的配置文件redis-master.conf啟動起來:src/redis-server redis-master.conf
3.搭建集群服務
為了保險起見最好先檢查下每台機器的redis服務是否正常啟動了ps -ef|grep redis
可以看見redis服務進程后面有個cluster的標志,普通啟動的redis服務是沒有這個標志的
5.0版本可以直接使用C語言客戶端提供的指令去構建集群:
src/redis-cli -a xxx --cluster create --cluster-replicas 1 192.168.0.67:6379 192.168.0.68:6379 192.168.0.69:6379 192.168.0.70:6379 192.168.0.71:6379 192.168.0.72:6379
-a 配置的密碼
--cluster create 表示集群創建
--cluster-replicas 表示每個master幾個slave,上面一共6個redis節點,因此會構建三個一主一從。
執行命令之前,如果你的redis環境以前搭建過主從或者哨兵之類的,數據不干凈可能會報錯,最好將持久化文件刪掉,然后flushdb,將以前臟數據清理掉,否則可能出現如下錯誤:
正常執行會返回一個集群分配計划,我們按照它的計划即可:
然后節點之間就開始通信構建集群,最后會看見16384個slots分配完畢,可以看見構建計划中有三個master,每個master都是有指定槽位的。意思就是存入的key經過crc16 hash算法之后得到的值,在哪個范圍內,就存儲到那個redis主從上面去,這就是redis的分片集群模式。
至此集群搭建完畢
4.集群操作
以集群方式連接redis客戶端通過cluster info查看集群信息,通過cluster nodes查看節點信息
src/redis-cli -a 密碼 -c 集群方式連接
我們設置set abc 123一個值 會看見客戶點會計算abc的slot是7638, 然后重定向到對應的主從的master上面去寫數據
現在我看下java客戶端的jedis里面的key值計算redis.clients.util.JedisClusterCRC16#getSlot(java.lang.String):
最后計算結果就會落到0-16383之間去。
當 Redis Cluster 的客戶端來連接集群時,它也會得到一份集群的槽位配置信息並將其緩存在客戶端本地。這樣當客戶 端要查找某個 key 時,可以直接定位到目標節點。同時因為槽位的信息可能會存在客戶端與服務器不一致的情況,還需 要糾正機制來實現槽位信息的校驗調整。
集中式集群和分片式集群
Redis節點之間使用的是gossip協議進行通信,每個節點之間都會互相通信。
gossip協議包含多種消息,包括ping,pong,meet,fail等等。
ping:每個節點都會頻繁給其他節點發送ping,其中包含自己的狀態還有自己維護的集群元數據,互相通過ping交換元數據;
pong: 返回ping和meet,包含自己的狀態和其他信息,也可以用於信息廣播和更新;
fail: 某個節點判斷另一個節點fail之后,就發送fail給其他節點,通知其他節點,指定的節點宕機了。
meet:某個節點發送meet給新加入的節點,讓新節點加入集群中,然后新節點就會開始與其他節點進行通信,不需要發送形成網絡的所需的所有CLUSTER MEET命令。發送CLUSTER MEET消息以便每個節點能夠達到其他每個節點只需通 過一條已知的節點鏈就夠了。由於在心跳包中會交換gossip信息,將會創建節點間缺失的鏈接。
gossip協議的優點在於元數據的更新比較分散,不是集中在一個地方,更新請求會陸陸續續,打到所有節點上去更新, 有一定的延時,降低了壓力;缺點在於元數據更新有延時可能導致集群的一些操作會有一些滯后。
就是自己提供服務的端口號+10000,比如6379,那么用於節點間通信 的就是16379端口。 每個節點每隔一段時間都會往另外幾個節點發送ping消息,同時其他幾點接收到ping消息之后返回pong消息。
還有就是集中式的,比如ZK集群
集中式的有點在於數據的更新和讀取,時效性非常好,一旦元數據出現變更立即就會更新到集中式(master)的存儲中,其他節點讀取的 時候立即就可以立即感知到;不足在於所有的元數據的更新壓力全部集中在一個地方,可能導致元數據的存儲壓力。
Redis集群選舉機制
當slave發現自己的master變為FAIL狀態時,便嘗試發起選舉,以期成為新的master。由於掛掉的master可能會有多個slave,從而存在多個slave競爭成為master節點的過程, 其過程如下:
1.slave發現自己的master變為FAIL
2.將自己記錄的集群currentEpoch(選舉輪次標記)加1,並廣播信息給集群中其他節點
3.其他節點收到該信息,只有master響應,判斷請求者的合法性,並發送結果
4.嘗試選舉的slave收集master返回的結果,收到超過半數master的統一后變成新Master
5.廣播Pong消息通知其他集群節點。
如果這次選舉不成功,比如三個小的主從A,B,C組成的集群,A的master掛了,A的兩個小弟發起選舉,結果B的master投給A的小弟A1,C的master投給了A的小弟A2,這樣就會發起第二次選舉,選舉輪次標記+1繼續上面的流程。事實上從節點並不是在主節點一進入 FAIL 狀態就馬上嘗試發起選舉,而是有一定延遲,一定的延遲確保我們等待FAIL狀態在集群中傳播,slave如果立即嘗試選舉,其它masters或許尚未意識到FAIL狀態,可能會拒絕投票。 同時下面公式里面的隨機數,也可以有效避免slave同時發起選舉,導致的平票情況。
•延遲計算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
•SLAVE_RANK表示此slave已經從master復制數據的總量的rank。Rank越小代表已復制的數據越新。這種方式下,持有最新數據的slave將會首先發起選舉(理論上)。
前面說到這種分片的集群模式的集群可以部分提供服務,當redis.conf的配置cluster-require-full-coverage為no時,表示當一個小主從整體掛掉的時候集群也可以用,也是說0-16383個槽位中,落在該主從對應的slots上面的key是用不了的,但是如果key落在其他的范圍是仍然可用的。