redis集群搭建(基于docker)
一、Redis Cluster(Redis集群)簡介
redis cluster是Redis的分布式解決方案,在3.0版本推出后有效地解決了redis分布式方面的需求自動將數據進行分片,每個master上放一部分數據提供內置的高可用支持,部分master不可用時,還是可以繼續工作的支撐N個redis master node,每個master node都可以掛載多個slave node高可用,因為每個master都有salve節點,那么如果master掛掉,redis cluster這套機制,就會自動將某個slave切換成master
二、redis cluster vs. replication + sentinal(主從復制讀寫分離模式)
如果你的數據量很少,主要是承載高并發高性能的場景,比如你的緩存一般就幾個G,單機足夠了replication,一個mater,多個slave,要幾個slave跟你的要求的讀吞吐量有關系,然后自己搭建一個sentinal集群,去保證redis主從架構的高可用性,就可以了redis cluster,主要是針對海量數據+高并發+高可用的場景,海量數據,如果你的數據量很大,那么建議就用redis cluster
- redis cluster的限制
- key 批量操作支持有限:類似mset、mget 操作,目前只支持對具有相同slot 值的key 執行批量操作。 對于映射為不同slot 值的key 由于執行mget、mget 等操作可能存在于多個節 點上,因此不被支持。
- key 事務操作支持有限:只支持多key 在同一節點上的事務操作,當多個key 分布在不同的節點上 時無法使用事務功能。
- key 作為數據分區的最小粒度
- 不能將一個大的鍵值對象如hash、list 等映射到不同的節點。
- 不支持多數據庫空間:單機下的Redis 可以支持16個數據庫(db0 ~ db15),集群模式下只能使用一 個數據庫空間,即db0。
- 復制結構只支持一層 :從節點只能復制主節點,不支持嵌套樹狀復制結構。
- 命令大多會重定向,耗時多
三、數據分布算法
比如你有 N 個 redis實例,那么如何將一個key映射到redis上呢,你很可能會采用類似下面的通用方法計算 key的 hash 值,然后均勻的映射到到 N 個 redis上:hash(key)%N,如果增加一個redis,映射公式變成了 hash(key)%(N+1),如果一個redis宕機了,映射公式變成了 hash(key)%(N-1),在這兩種情況下,幾乎所有的緩存都失效了(緩存大部分命中不了)。會導致數據庫訪問的壓力陡增,嚴重情況,還可能導致數據庫宕機。關于這點可以參考這篇文章。
一致性哈希算法(Consistent Hashing)最早在論文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。簡單來說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間為0 - 2的32次方-1(即哈希值是一個32位無符號整形)
最大的好處:一個master宕機不會導致大部分緩存失效,可能存在緩存熱點問題
四、redis cluster的hash slot算法
redis cluster有固定的16384個hash slot,對每個key計算CRC16值,然后對16384取模,可以獲取key對應的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,可以對指定的數據,讓他們走同一個hash slot,通過hash tag來實現
可以將槽0-5000指派給節點7000負責。每個節點都會記錄哪些槽指派給了自己,哪些槽指派給了其他節點??蛻舳讼蚬濣c發送鍵命令,節點要計算這個鍵屬于哪個槽。如果是自己負責這個槽,那么直接執行命令,如果不是,向客戶端返回一個MOVED錯誤,指引客戶端轉向正確的節點。
- redis cluster 多master的寫入
在redis cluster寫入數據的時候,其實是你可以將請求發送到任意一個master上去執行。但是,每個master都會計算這個key對應的CRC16值,然后對16384個hashslot取模,找到key對應的hashslot,找到hashslot對應的master,如果對應的master就在自己本地的話,set mykey1 v1,mykey1這個key對應的hashslot就在自己本地,那么自己就處理掉了。但是如果計算出來的hashslot在其他master上,那么就會給客戶端返回一個moved error,告訴你,你得到哪個master上去執行這條寫入的命令。
什么叫做多master的寫入,就是每條數據只能存在于一個master上,不同的master負責存儲不同的數據,分布式的數據存儲100w條數據,5個master,每個master就負責存儲20w條數據,分布式數據存儲。默認情況下,redis cluster的核心的理念,主要是用slave做高可用的,每個master掛一兩個slave,主要是做數據的熱備,還有master故障時的主備切換,實現高可用的。redis cluster默認是不支持slave節點讀或者寫的,跟我們手動基于replication搭建的主從架構不一樣的jedis客戶端,對redis cluster的讀寫分離支持不太好的,默認的話就是讀和寫都到master上去執行的,如果你要讓最流行的jedis做redis cluster的讀寫分離的訪問,那可能還得自己修改一點jedis的源碼,成本比較高。讀寫分離,是為了什么,主要是因為要建立一主多從的架構,才能橫向任意擴展slave node去支撐更大的讀吞吐量。redis cluster的架構下,實際上本身master就是可以任意擴展的,你如果要支撐更大的讀吞吐量,或者寫吞吐量,或者數據量,都可以直接對master進行橫向擴展就可以了
五、節點間的內部通信機制
-
(1)redis cluster節點間采取gossip協議進行通信
- 跟集中式不同,不是將集群元數據(節點信息,故障,等等)集中存儲在某個節點上,而是互相之間不斷通信,保持整個集群所有節點的數據是完整的
- 集中式:好處在于,元數據的更新和讀取,時效性非常好,一旦元數據出現了變更,立即就更新到集中式的存儲中,其他節點讀取的時候立 即就可以感知到;不好在于,所有的元數據的跟新壓力全部集中在一個地方,可能會導致元數據的存儲有壓力
- gossip:好處在于,元數據的更新比較分散,不是集中在一個地方,更新請求會陸陸續續,打到所有節點上去更新,有一定的延時,降低了壓力;缺點,元數據更新有延時,可能導致集群的一些操作會有一些滯后
-
(2)10000端口
- 每個節點都有一個專門用于節點間通信的端口,就是自己提供服務的端口號+10000,比如7001,那么用于節點間通信的就是17001端口
- 每隔節點每隔一段時間都會往另外幾個節點發送ping消息,同時其他幾點接收到ping之后返回pong
-
(3)交換的信息:故障信息,節點的增加和移除,hash slot信息,等等
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給其他節點,通知其他節點,指定的節點宕機了
ping很頻繁,而且要攜帶一些元數據,所以可能會加重網絡負擔,每個節點每秒會執行10次ping,每次會選擇5個最久沒有通信的其他節點。當然如果發現某個節點通信延時達到了cluster_node_timeout / 2,那么立即發送ping,避免數據交換延時過長,落后的時間太長了。比如說,兩個節點之間都10分鐘沒有交換數據了,那么整個集群處于嚴重的元數據不一致的情況,就會有問題。所以cluster_node_timeout可以調節,如果調節比較大,那么會降低發送的頻率,每次ping,一個是帶上自己節點的信息,還有就是帶上1/10其他節點的信息,發送出去,進行數據交換,至少包含3個其他節點的信息,最多包含總節點-2個其他節點的信息
六、基于重定向的客戶端
請求重定向
客戶端可能會挑選任意一個redis實例去發送命令,每個redis實例接收到命令,都會計算key對應的hash slot。如果在本地就在本地處理,否則返回moved給客戶端,讓客戶端進行重定向cluster keyslot mykey,可以查看一個key對應的hash slot是什么。用redis-cli的時候,可以加入-c參數,支持自動的請求重定向,redis-cli接收到moved之后,會自動重定向到對應的節點執行命令
計算hash slot
計算hash slot的算法,就是根據key計算CRC16值,然后對16384取模,拿到對應的hash slot。用hash tag可以手動指定key對應的slot,同一個hash tag下的key,都會在一個hash slot中,比如set mykey1:{100}和set mykey2:{100}
hash slot查找
節點間通過gossip協議進行數據交換,就知道每個hash slot在哪個節點上
七、smart jedis
什么是smart jedis
基于重定向的客戶端,很消耗網絡IO,因為大部分情況下,可能都會出現一次請求重定向,才能找到正確的節點。所以大部分的客戶端,比如java redis客戶端,就是jedis,都是smart的。本地維護一份hashslot -> node的映射表,緩存,大部分情況下,直接走本地緩存就可以找到hashslot -> node,不需要通過節點進行moved重定向。
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,都進行了優化,避免了類似問題
hashslot遷移和ask重定向
如果hash slot正在遷移,那么會返回ask重定向給jedis。jedis接收到ask重定向之后,會重新定位到目標節點去執行,但是因為ask發生在hash slot遷移過程中,所以JedisCluster API收到ask是不會更新hashslot本地緩存。已經可以確定說,hashslot已經遷移完了,moved是會更新本地hashslot->node映射表緩存的
八、高可用性與主備切換原理
redis cluster的高可用的原理,幾乎跟哨兵是類似的
- 如果一個節點認為另外一個節點宕機,那么就是pfail,主觀宕機
- 如果多個節點都認為另外一個節點宕機了,那么就是fail,客觀宕機,跟哨兵的原理幾乎一樣,sdown,odown
- 在cluster-node-timeout內,某個節點一直沒有返回pong,那么就被認為pfail
- 如果一個節點認為某個節點pfail了,那么會在gossip ping消息中,ping給其他節點,如果超過半數的節點都認為pfail了,那么就會變成fail
從節點過濾
對宕機的master node,從其所有的slave node中,選擇一個切換成master node,檢查每個slave node與master node斷開連接的時間,如果超過了cluster-node-timeout * cluster-slave-validity-factor,那么就沒有資格切換成master。這個也是跟哨兵是一樣的,從節點超時過濾的步驟
從節點選舉
- 哨兵:對所有從節點進行排序,slave priority,offset,run id
- 每個從節點,都根據自己對master復制數據的offset,來設置一個選舉時間,offset越大(復制數據越多)的從節點,選舉時間越靠前,優先進行選舉
- 所有的master node開始slave選舉投票,給要進行選舉的slave進行投票,如果大部分master node(N/2 +1)都投票給了某個從節點,那么選舉通過,那個從節點可以切換成master
- 從節點執行主備切換,從節點切換為主節點
整個流程跟哨兵相比,非常類似,所以說,redis cluster功能強大,直接集成了replication和sentinal的功能
九、基于docker搭建redis cluster(三主三從)
在docker里創建專用于redis_cluster的網絡
使用腳本進行創建redis
查看docker給每個redis節點的網絡分配
如此便是成功了
- 測試:以集群的方式鏈接redis
查看集群狀態:
查看各節點狀態:
可以看到是三主三從的模式
可以看到,如果hash值算出來后不是對應本機,就進行redirect到其他節點保存
主機和從機的數據也同步了
集群不能用的情況:
- (1)有半數或者半數以上的master掛掉,集群就不能用了
- (2)如果集群任意master掛掉,且當前master沒有slave,集群不能用
- 注:一個主庫掛掉,它的從庫自動頂替為主庫,正常使用(前提是:有半數或者半數以上的master能用),掛掉的主庫修復好后,會成為從庫,不會搶占為主
十、重點注意
如果集群是要供公網訪問,那么redis.conf文件中的cluster-announce-ip屬性一定要配置真實ip,不然外部訪問時返回的集群信息是內部ip,如下所示:
可以看這篇文章,我也是使用redisson時遇到這個問題。
我的解決如下:
創建集群命令
redis-cli --cluster create 真實ip:9004 真實ip:9003 真實ip:9002 真實ip:9001 真實ip:9005 真實ip:9006 --cluster-replicas 1解決后的返回信息
這個問題解決方式更詳細參考
參考文章
參考文章
總結
以上是生活随笔為你收集整理的redis集群搭建(基于docker)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一致性哈希算法在分布式缓存中的应用
- 下一篇: 互联网日报 | 3月13日 星期六 |