redis集羣的搭建
本文使用的是3臺主機,每臺主機2個節點,3臺maser,3臺salve的環境。
1. 環境介紹:
3臺CentOS release 6.9服務器
192.168.1.35 192.168.1.36 192.168.1.37
redis-4.0.9.tar.gz
ruby-2.4.4.tar.gz
2. 軟件安裝
安裝redis:
本文中的環境默認是不能連接外網的。所以軟件包只能通過跳板機或者其他的機器通過scp上傳。
編譯安裝:
tar -xf redis-4.0.9.tar.gz mkdir -p /usr/local/redis mv redis-4.0.9/* /usr/local/redis/ cd /usr/local/redis/ make && make install
創建redis節點:
mkdir -p redis_cluster/{7000,7001} cp redis.conf redis_cluster/7000 cp redis.conf resis_cluster/7001
分別修改7000/7001裏面的配置文件的內容:
port 7000 //端口7000,7001 bind 192.168.1.35 //改爲35,36,37 daemonize yes //redis後臺運行 pidfile /var/run/redis_7000.pid //pid對應7000,7001 cluster-enabled yes //開啓集羣 cluster-config-file nodes_7000.conf //指定集羣的配置文件,自動生成 cluster-node-timeout 15000 //設置集羣超時時間 appendonly yes //aof日誌開啓,它會每次寫操作都記錄一條日誌
在另外的兩臺機器上重複上面的配置規則
啓動各個節點(另外兩臺也是同樣地):
redis-server redis_cluster/7000/redis.conf
redis-server redis_cluster/7001/redis.conf
查看各個節點是否起來: $ ss -tnlp|grep redis LISTEN 0 511 192.168.1.35:17000 *:* users:(("redis-server",24909,9)) LISTEN 0 511 192.168.1.35:17001 *:* users:(("redis-server",24914,9)) LISTEN 0 511 192.168.1.35:7000 *:* users:(("redis-server",24909,6)) LISTEN 0 511 192.168.1.35:7001 *:* users:(("redis-server",24914,6))
安裝ruby:
tar -xf ruby-2.4.4.tar.gz cd ruby-2.4.4 ./configure --prefix=/usr/local/ruby make && make install /usr/local/ruby/bin/gem install redis
或者
安裝gem和redis的插件(https://rubygems.global.ssl.fastly.net/gems/redis-3.2.2.gem) # gem install -l redis-3.2.2.gem
3. 集羣創建
redis-trib.rb是redis官方推出的redis集羣管理的工具,這個工具在redis的src目錄下。
/usr/local/ruby/bin/ruby /usr/local/redis/src/redis-trib.rb create --replicas 1 192.168.1.35:7000 192.168.1.35:7001 192.168.1.36:7000 192.168.1.36:70001 192.168.1.37:7000 192.168.1.37:7001
--replicas:指定每個主節點有幾個從節點 注意:需要有3個或者以上的主節點,否則在創建集羣是會失敗,並且存活的主節點數小於總節點數的一半時,整個集羣就無法提供服務了。 原理:redis cluster在設計的時候,就考慮到了去中心化去中間件,就是說,集羣中的每個節點都是平等的關係,都是對等的,每個節點都保存各自的數據和整個集羣的狀態。每個節點都和其他所有節點連接,而且這些連接保持活躍,這樣就保證了我們只需要連接集羣中的任意一個節點,就可以獲取到其他節點的數據。 Redis 集羣沒有並使用傳統的一致性哈希來分配數據,而是採用另外一種叫做哈希槽 (hash slot)的方式來分配的。redis cluster 默認分配了 16384 個slot,當我們set一個key 時,會用CRC16算法來取模得到所屬的slot,然後將這個key 分到哈希槽區間的節點上,具體算法就是:CRC16(key) % 16384。所以我們在測試的時候看到set 和 get 的時候,直接跳轉到了7000端口的節點。 Redis 集羣會把數據存在一個 master 節點,然後在這個 master 和其對應的salve 之間進行數據同步。當讀取數據時,也根據一致性哈希算法到對應的 master 節點獲取數據。只有當一個master 掛掉之後,纔會啓動一個對應的 salve 節點,充當 master 。
4.集羣驗證:
可以通過隨便在一臺機器上創建數據來驗證整個集羣。
通過redis-cli命令來登陸 $ redis-cli -h 192.168.1.35 -c -p 7000 192.168.1.37:7000> set hello world -> Redirected to slot [866] located at 192.168.1.35:7000 OK 192.168.1.35:7000> keys * 1) "hello"
然後登陸另外一臺的7000端口,查看key的內容。
$ redis-cli -h 192.168.1.36 -c -p 7000 192.168.1..36:7000> get hello -> Redirected to slot [866] located at 192.168.1.35:7000 "world" 192.168.1.35:7000>
如果其他節點可以讀取得到就說明集羣運行正常。
5.redis集羣管理工具redis-trib.rb
redis-trib.rb是redis官方推出的管理redis集羣的工具,集成在redis的源碼src目錄下,是基於redis提供的集羣命令封裝成簡單、便捷、實用的操作工具。
redis-trib.rb的功能參數:
Usage: redis-trib <command> <options> <arguments ...> create host1:port1 ... hostN:portN --replicas <arg> check host:port info host:port fix host:port --timeout <arg> reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout <arg> --pipeline <arg> rebalance host:port --weight <arg> --auto-weights --use-empty-masters --timeout <arg> --simulate --pipeline <arg> --threshold <arg> add-node new_host:new_port existing_host:existing_port --slave --master-id <arg> del-node host:port node_id set-timeout host:port milliseconds call host:port command arg arg .. arg import host:port --from <arg> --copy --replace help (show this help) For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
簡單來看:redis-trib.rb提供了以下的功能:
create | 創建集羣 |
check | 檢查集羣 |
info | 查看集羣信息 |
fix | 修復集羣 |
reshard | 在線遷移集羣 |
rebalance | 平衡集羣節點的slot數量 |
add-node | 將節點加入新的集羣 |
del-node | 從集羣中刪除節點 |
set-timout | 設置集羣節點間的心跳連接超時時間 |
call | 在集羣全部節點上執行命令 |
import | 將外部的redis數據導入集羣 |
create創建集羣:
可選參數爲replicas,表示需要幾個slave。
簡單用法:
ruby redis-trib.rb create 192.168.1.20:7000 192.168.1.21:7000 192.168.1.22:7000
有一個從節點的創建命令:
ruby redis-trib.rb create --replicas 1 192.168.1.20:7000 192.168.1.21:7000 192.168.1.22:7000 192.168.1.23:7000 192.168.1.24:7000 192.168.1.25:7000
創建流程如下:
1、首先爲每個節點創建ClusterNode對象,包括連接每個節點。檢查每個節點是否爲獨立且db爲空的節點。執行load_info方法導入節點信息。
2、檢查傳入的master節點數量是否大於等於3個。只有大於3個節點才能組成集羣。
3、計算每個master需要分配的slot數量,以及給master分配slave。分配的算法大致如下: 先把節點按照host分類,這樣保證master節點能分配到更多的主機中。 不停遍歷遍歷host列表,從每個host列表中彈出一個節點,放入interleaved數組。直到所有的節點都彈出爲止。 master節點列表就是interleaved前面的master數量的節點列表。保存在masters數組。 計算每個master節點負責的slot數量,保存在slots_per_node對象,用slot總數除以master數量取整即可。 遍歷masters數組,每個master分配slots_per_node個slot,最後一個master,分配到16384個slot爲止。 接下來爲master分配slave,分配算法會盡量保證master和slave節點不在同一臺主機上。對於分配完指定slave數量的節點,還有多餘的節點,也會爲這些節點尋找master。分配算法會遍歷兩次masters數組。 第一次遍歷masters數組,在餘下的節點列表找到replicas數量個slave。每個slave爲第一個和master節點host不一樣的節點,如果沒有不一樣的節點,則直接取出餘下列表的第一個節點。 第二次遍歷是在對於節點數除以replicas不爲整數,則會多餘一部分節點。遍歷的方式跟第一次一樣,只是第一次會一次性給master分配replicas數量個slave,而第二次遍歷只分配一個,直到餘下的節點被全部分配出去。
4、打印出分配信息,並提示用戶輸入“yes”確認是否按照打印出來的分配方式創建集羣。
5、輸入“yes”後,會執行flush_nodes_config操作,該操作執行前面的分配結果,給master分配slot,讓slave複製master,對於還沒有握手(cluster meet)的節點,slave複製操作無法完成,不過沒關係,flush_nodes_config操作出現異常會很快返回,後續握手後會再次執行flush_nodes_config。
6、給每個節點分配epoch,遍歷節點,每個節點分配的epoch比之前節點大1。
7、節點間開始相互握手,握手的方式爲節點列表的其他節點跟第一個節點握手。
8、然後每隔1秒檢查一次各個節點是否已經消息同步完成,使用ClusterNode的get_config_signature方法,檢查的算法爲獲取每個節點cluster nodes信息,排序每個節點,組裝成node_id1:slots|node_id2:slot2|...的字符串。如果每個節點獲得字符串都相同,即認爲握手成功。
9、此後會再執行一次flush_nodes_config,這次主要是爲了完成slave複製操作。
10、最後再執行check_cluster,全面檢查一次集羣狀態。包括和前面握手時檢查一樣的方式再檢查一遍。確認沒有遷移的節點。確認所有的slot都被分配出去了。
11、至此完成了整個創建流程,返回[OK]
check檢查集羣
使用check來檢查集羣的狀態。只需要選擇其中的一個節點即可:
$ /usr/local/ruby/bin/ruby /usr/local/redis/src/redis-trib.rb check 192.168.1.36:7000 >>> Performing Cluster Check (using node 192.168.1.36:7000) M: 631e3b56a0895f5ac62b15ad7467752877d8079e 1192.168.1.36:7000 slots:5461-10922 (5462 slots) master 1 additional replica(s) M: d35e7bd3a31054b96dfe2d08d7472731753d6ceb 192.168.1.37:7000 slots:10923-16383 (5461 slots) master 1 additional replica(s) M: 06fbb8062252b78093d4ef50188199a580bb86bd 192.168.1.35:7000 slots:0-5460 (5461 slots) master 1 additional replica(s) S: 4e26eac416d32421800f91f78a9df070aa7d0855 192.168.1.35:7001 slots: (0 slots) slave replicates d35e7bd3a31054b96dfe2d08d7472731753d6ceb S: 8090354b1b3575ae1e331841964c6c33c55897da 192.168.1.37:7001 slots: (0 slots) slave replicates 631e3b56a0895f5ac62b15ad7467752877d8079e S: 0909bb82f88e0755bab3bc06f1cc3b4325c2a869 192.168.1.36:7001 slots: (0 slots) slave replicates 06fbb8062252b78093d4ef50188199a580bb86bd [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
info查看集羣的信息
info命令用來查看集羣的信息,同樣只需要指定一個節點即可:
/usr/local/ruby/bin/ruby /usr/local/redis/src/redis-trib.rb info 192.168.1.36:7000 192.168.1.36:7000 (631e3b56...) -> 1 keys | 5462 slots | 1 slaves. 192.168.1.37:7000 (d35e7bd3...) -> 0 keys | 5461 slots | 1 slaves. 192.168.1.35:7000 (06fbb806...) -> 1 keys | 5461 slots | 1 slaves. [OK] 2 keys in 3 masters. 0.00 keys per slot on average.
fix修復集羣:
fix命令的流程跟check的流程很像,顯示加載集羣信息,然後在check_cluster方法內傳入fix爲 true的變量,會在集羣檢查出現異常的時候執行修復流程。目前fix命令能修復兩種異常,一種是集羣有處於遷移中的slot的節點,一種是slot未完全分配的異常。
fix_open_slot方法是修復集羣有處於遷移中的slot的節點異常:
1、先檢查該slot是誰負責的,遷移的源節點如果沒完成遷移,owner還是該節點。沒有owner的slot無法完成修復功能。
2、遍歷每個節點,獲取哪些節點標記該slot爲migrating狀態,哪些節點標記該slot爲importing狀態。對於owner不是該節點,但是通過cluster countkeysinslot獲取到該節點有數據的情況,也認爲該節點爲importing狀態。
3、如果migrating和importing狀態的節點均只有1個,這可能是遷移過程中redis-trib.rb被中斷所致,直接執行move_slot繼續完成遷移任務即可。傳遞dots和fix爲true。
4、如果migrating爲空,importing狀態的節點大於0,那麼這種情況執行回滾流程,將importing狀態的節點數據通過move_slot方法導給slot的owner節點,傳遞dots、fix和cold爲true。接着對importing的節點執行cluster stable命令恢復穩定。
5、如果importing狀態的節點爲空,有一個migrating狀態的節點,而且該節點在當前slot沒有數據,那麼可以直接把這個slot設爲stable。
6、如果migrating和importing狀態不是上述情況,目前redis-trib.rb工具無法修復,上述的三種情況也已經覆蓋了通過redis-trib.rb工具遷移出現異常的各個方面,人爲的異常情形太多,很難考慮完全。 fix_slots_coverage方法能修復slot未完全分配的異常:
未分配的slot有三種狀態:
1、所有節點的該slot都沒有數據。該狀態redis-trib.rb工具直接採用隨機分配的方式,並沒有考慮節點的均衡。本人嘗試對沒有分配slot的集羣通過fix修復集羣,結果slot還是能比較平均的分配,但是沒有了連續性,打印的slot信息非常離散。
2、有一個節點的該slot有數據。該狀態下,直接把slot分配給該slot有數據的節點。
3、有多個節點的該slot有數據。此種情況目前還處於TODO狀態,不過redis作者列出了修復的步驟,對這些節點,除第一個節點,執行cluster migrating命令,然後把這些節點的數據遷移到第一個節點上。清除migrating狀態,然後把slot分配給第一個節點。
reshard在線遷移slot:
reshard命令可以在線把集羣的一些slot從集羣原來slot負責節點遷移到新的節點,利用reshard可以完成集羣的在線橫向擴容和縮容。
reshard的參數介紹:
reshard host:port 指定從哪個節點獲取集羣的信息 --from 需要從哪些節點上遷移slot。--from all 所有節點 --to slot需要遷移的目的節點node id --slots 需要遷移的slot數量 --yes 設置該參數,會提示輸入yes後再執行reshard --timeout 設置migrate命令的超時時間 --pipeline 定義cluster getkeysinslot一次取出的key數量,默認爲10
參考文獻
redis集羣教程
redis集羣規範
https://redis.io/topics/cluster-spec
https://redis.io/topics/cluster-tutorial