官方文章:
https://redis.io/topics/cluster-tutorial#redis-cluster-configuration-parameters
本文永久地址:
https://www.cnblogs.com/erbiao/p/9177241.html
本文檔是Redis集群的簡單介紹,沒有涉及復雜難懂的分布式概念的贅述,只是提供了從用戶角度來如何搭建測試以及使用的方法,若你打算使用並深入了解Redis集群,推薦閱讀完本章節后,仔細閱讀Redis集群規范一章。本教程試圖提供最終用戶一個簡單的關於集群和一致性特征的描述。
請注意,本教程基於Redis 3.0或更高的版本。如果你計划部署集群,那么我們建議你從閱讀這個文檔開始。
Redis集群基礎
Redis集群定義
Redis集群提供一種redis運行方式,能將數據自動分片到多個Redis節點,且Redis集群在分區期間也提供一定的可用性,即在某些節點發生故障或無法通信時,集群也能正常工作,但當大面積節點故障,如大多數master都不可用時,集群就不能使用了。
從實用性角度,Redis集群提供一下功能:
·自動切割數據到多個Redis節點
·小部分節點故障或者不可達時,集群能正常工作
Redis集群TCP端口
Redis集群中每個節點都需偵聽兩個TCP端口,6379端口用於客戶端通信,客戶端通信端口加上10000(兩個端口總是相差10000),如16379專用於集群總線(Cluster bus),用於節點間二進制協議的節點間通信。各節點使用集群總線故障檢測,配置更新,故障轉移授權等。客戶端不能使用集群總線端口。
請注意,為了讓Redis群集正常工作,需要為每個節點配置2個端口(必須):
·客戶端端口(默認6379)需要對所有客戶端和集群節點開放,因為集群節點需要通過該端口進行密鑰遷移(keys migrations)
·集群總線端口(客戶端端口+ 10000)對集群所有節點開放即可
集群總線使用不同的二進制協議(不同於客戶端與Redis使用的協議)進行節點到節點的數據交換,此協議可以減少帶寬和處理時間
Redis集群和Docker
目前,redis集群並不支持地址和端口都被重新映射的NAT環境。
Docker使用一種端口映射技術:運行在docker容器內部的程序可能使用的端口和客戶端程序使用的端口不同,這對於同一服務器中同時使用相同端口運行多個容器很有用
為了使Docker與Redis Cluster兼容,您需要使用Docker的主機聯網模式。請查看Docker文檔的--net=host選項以獲取更多信息。
Redis集群數據分片
Redis集群不使用一致性哈希,而是一種不同的分片形式,其中每個鍵在概念上都是我們稱之為哈希槽的部分。
Redis集群中有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽。
集群中每個節點負責哈希槽的的一個子集,如一個有3點歌節點的Redis集群:
·節點A包含從0到5500的哈希槽
·節點B包含從5501到11000的哈希槽
·節點C包含從11001到16383的哈希槽
如此便能方便地添加和刪除集群中的節點。如我想添加一個新節點D,我需要將節點A,B,C中的一些哈希槽移動到D;同樣,想刪除節點A,則可以移動由A服務的散列槽到B和C.當節點A將為空時,我可以將它從群集中徹底刪除。
由於從一個節點將哈希槽移動到另一個節點並不會停止服務,所以無論添加刪除或者改變某個節點的哈希槽的數量都不會造成集群不可用的狀態
若多個key都屬於一個哈希槽,那么集群能通過單個命令(或者一個完成的事務或Lua腳本執行)同時操作這些key,我們可以使用“哈希標簽”(Hash tags)強制讓多個key分配到同一哈希槽。“哈希標簽”在Redis集群規范中有介紹,簡單來說就是若key含有大括號”{}”,則只有大括號中的字符串會被哈希,如“this{foo}和another{foo}”這兩個key一定會被分配到同一個哈希槽,然后在具有多個間的命令中作為參數一起使用。
Redis集群主從模式
為了保證在部分主master節點掛掉或不能與大多數節點通信時保持可用性,Redis集群使用主從模式,保證每個哈希槽都有1個到N個副本。
上述例子我們有節點A,B,C三個,若節點B故障,集群則不再可用,且會丟失從5501到11000哈希槽。然而若每個master都有一個slave,也就是說A,B,C三個master節點就會分別有A1,B1,C1三個slave節點,即使B節點掛掉,集群也不會受影響,B1復制了B的數據,此時集群將提升B1作為新master繼續正常工作。但B和B1同時掛掉,redis集群肯定就不可用了。
Redis集群對一致性的保證
Redis集群不能保證強一致性,在架構中這意味着在某些情況下,可能會丟失向客戶端返回“確認寫成功”的數據。
丟失寫入數據的第一個原因就是redis異步復制數據,也就是說:
·客戶端寫入數據到master B
·master B向客戶端返回“寫入成功”
·master B傳輸數據到slave:B1,B2,B3
由上可知,B在回復客戶端之前不等待B1,B2,B3的寫入完成確認,因此這會對redis造成嚴重的延遲損失,就像客戶端寫入了數據,master B確認寫入,但在B將數據復制到任何一個slave前就已經掛掉,那寫入將永久丟失,因為被提升為新master的任何slave都沒有收到該數據。
這與大多數每秒刷新數據到磁盤的數據庫配置類似,也可以通過強制數據庫在回復客戶端前先刷新數據到磁盤來提高一致性,但這種做法性能很差,這種方式就是讓Redis集群在使用同步復制。
到底使用哪種方式,要根據應用場景在性能和一致性之間有一個權衡。
若真的需要,redis集群也能通過“WAIT”指令實現同步復制,這使得寫入丟失的可能行大大降低,但並不意味着使用同步復制Redis集群就是強一致性了,如master還未來得及同步數據到任一slave就掛掉,或者被提升為新master沒有收到同步過來的數據,都會導致不一致性的發生。使用“WAIT”只能相對的提高一致性。
還有一個值得注意的場景發生在網絡故障時,當客戶端與少數節點(至少一個master節點)正常通信,但與其他大多數節點網絡不通。以A,B,C,A1,B1,C1 三主三從組成的6個節點集群為例,另外一個客戶端稱之為Z1。網絡故障產生網絡分區后,可能A,C,A1,B1,C1在一個分區,而B和Z1在另一個分區。此時B仍然能接收寫入請求,Z1仍然能寫入B。若分區在很短時間能恢復,集群將繼續正常運行,但若網絡太久未恢復,網絡分區持續時間太長,B1會在大多數節點都能通信的分區里面被提升為master,master B在網絡恢復后,會成為master B1的新slave,且丟棄Z1發送給B的寫操作,然后從B1重新復制數據。
節點間網絡通信斷開有一個時間限制,若斷開時間超過了大多數節點能容忍的長度,那就會有一個新的master被選舉出來。
這個時間被稱之為節點超時(node timeout),這個時間對集群非常重要,當達到節點超時時間,master被認為已經下線,會有新master被選舉出來。同樣,在節點超時后,若master仍然不能聯系到其他master,它將進入錯誤狀態,並停止接收寫入請求。
Redis集群參數配置
先介紹redis.conf中集群的參數配置,后面會有集群部署示例。
·cluster-enabled yes/no #Redis集群開關。Yes表示他是集群中一個節點,no表示他是一個普通單一的redis實例
·cluster-config-file nodes-6379.conf #配置被稱之為“集群配置文件”,但此配置文件是集群節點自動維護,不需要人工干預,每次集群配置有所變動,都會自動更新到此文件,以便節點重啟時能重新加載配置。該文件列出集群中其他節點的狀態,持久化的參數選項等等
·cluster-node-timeout 15000 #單位毫秒,集群節點能夠失聯的最大時間,超過該時間,該節點就會被認為故障。Master超過此時間不可達,它的slave節點就會選舉出新master替代之。另外,任何節點超過此時間沒有與大部分master通信將停止接收任何請求
·cluster-slave-validity-factor 10 #用於限定slave與master的失聯時長的倍數。若設置0,無論slave與master失聯多久,slave都會嘗試升級為master(只要slave無法與master通訊,都會嘗試故障轉移,Cluster有選舉機制,有可能會被否決),若設置為正數,則失聯的最大時長為(cluster-node-timeout * cluster-slave-validity-factor),超過此時間slave不具備故障轉移資格,無法被提升為master。如cluster-node-timeout為5s,cluster-slave-validity-factor為10,,當slave與master失聯超過50s后,slave就不再具備成為master的資格。注意,如果此參數不為0,可能出現由於某master節點失聯卻沒有slave能頂上的情況,從而導致集群不能正常工作,此時,只有等到原來的master重新回歸到集群,集群才恢復正常。
·cluster-migration-barrier 1 #此參數保證Redis集群中不會出現裸奔的master節點,即保證每個master節點都有slave節點。只有當一個master節點至少擁有給定數量個處於正常工作中的slave節點時,才會分配slave節點給集群中孤立的master節點。這個給定數量就是cluster-migration-barrier。給定數量是1意味着一個slave節點只有在其master節點另外至少還有一個正常工作的slave節點的情況下才會被分配(副本遷移)。那些分配后仍然剩余migration barrier個slave節點的master節點才會觸發副本遷移,而不是分配前有migration barrier個slave節點的master節點會觸發節點分配!!有關教程,請參閱本文檔副本遷移部分
·cluster-require-full-coverage yes #在部分key所在的節點不可用時,如果此參數設置為”yes”(默認值), 則整個集群停止接受操作;如果此參數設置為”no”,則集群依然為可達節點上的key提供讀操作(集群是否需要所有的slot都分配給在線節點才能正常訪問)
創建和使用Redis集群(主要兩步驟:創建實例和連接實例創建集群)
注意:手動部署一個redis集群,最主要是在學習各種操作。
若想盡快啟動並運行一個集群,可以直接看下一節“使用create-cluster腳本直接創建Redis群集”
要創建一個redis集群,首要任務就是以集群模式運行幾個空Redis實例,必須以集群模式運行,才能使Redis實例具有集群節點功能、支持集群節點指令。
以下是最小redis集群配置文件:
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
Redis集群正常運行至少需要3個master,對於第一次測試,建議啟動一個由三主三從組成的六節點群集:
1、創建6個以redis實例端口名稱的目錄:
mkdir -pv /usr/local/cluster-test/700{0..5}
2、在每個端口號目錄下創建配置文件redis700{0..5}.conf,內容為上面最小redis集群配置,注意修改端口號
3、啟動6個redis集群節點,建議啟動6個終端分別啟動:
cd /usr/local/cluster-test/7000 ; redis-server /usr/local/cluster-test/7000/redis7000.conf & cd /usr/local/cluster-test/7001 ; redis-server /usr/local/cluster-test/7001/redis7001.conf & cd /usr/local/cluster-test/7002 ; redis-server /usr/local/cluster-test/7002/redis7002.conf & cd /usr/local/cluster-test/7003 ; redis-server /usr/local/cluster-test/7003/redis7003.conf & cd /usr/local/cluster-test/7004 ; redis-server /usr/local/cluster-test/7004/redis7004.conf & cd /usr/local/cluster-test/7005 ; redis-server /usr/local/cluster-test/7005/redis7005.conf &
由於每個實例都是新啟動的,nodes.conf還沒有自動生成,所以會發出此信息,且生成nodes.conf,都會為自己分配一個新的ID:
10390:M 31 May 14:33:58.078 * No cluster configuration found, I'm f219739744e2fca6f2e6e6c75c9c1f2caa95b05b
作為此節點在整個集群中的唯一標識,生成的ID將一直被各節點使用,節點使用ID來區分其他節點而非IP+PORT,這個ID在節點的整個生命周期內都不會改變,除非節點被移除集群。這個ID我們稱之為節點ID(Node ID)。
4、此時6個集群模式的Redis實例已經運行,可以開始創建Redis集群。Ruby腳本“redis-trib”是一個可以讓我們非常方便地創建Redis集群的命令行工具,該腳本用於創建新集群,狀態檢查,或給集群重新分片:
yum install rubygems ruby
gem install redis #若提示ruby版本過低,請查看 https://www.cnblogs.com/erbiao/p/9117018.html 。另外,若你的redis版本是4.0.X,強烈建議安裝低版本的redis版本庫,否則reshard時會出現語法錯誤。具體請查看:https://www.cnblogs.com/erbiao/p/9138604.html
redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 #“--replicas 1”表示每個master節點需要一個slave節點。其他參數就是需要加入這個集群的redis實例的地址。執行后,redis-trib會提供一些建議的配置。
... ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered. #表示有16384個哈希槽可用
顯示如上信息表示集群創建完成
5、查看nodes.conf信息
cat /usr/local/cluster-test/7001/nodes.conf
06c5baf38ae368bbe8b7fef037d42026ab385590 127.0.0.1:7001@17001 myself,master - 0 1527753131000 2 connected 5461-10922
fdc0c79f9f782b218dad297b48bde32aa0acc31f 127.0.0.1:7005@17005 slave 3fbbebd7bd822dfada7db5bde983534ff2906d4f 0 1527753131000 6 connected
b88654ae05706a771d6f23d8877e890f99dba315 127.0.0.1:7004@17004 slave 06c5baf38ae368bbe8b7fef037d42026ab385590 0 1527753131000 5 connected
41ec008599691424cdf8cdf9f3dc26ab1eef7050 127.0.0.1:7003@17003 slave f219739744e2fca6f2e6e6c75c9c1f2caa95b05b 0 1527753131600 4 connected
f219739744e2fca6f2e6e6c75c9c1f2caa95b05b 127.0.0.1:7000@17000 master - 0 1527753131000 1 connected 0-5460
3fbbebd7bd822dfada7db5bde983534ff2906d4f 127.0.0.1:7002@17002 master - 0 1527753130599 3 connected 10923-16383
vars currentEpoch 6 lastVoteEpoch 0
使用腳本create-cluster創建redis集群
上述的創建方法比較繁瑣,“utils/create-cluster/create-cluster”此腳本能簡化創建步驟,此腳本會調用其他redis集群命令,所以最好在utils/create-cluster中執行,或者修改腳本文件也是可以的。
腳本“utils/create-cluster/create-cluster”有默認參數,可以根據需要重新定義。
如要啟動一個三主三從6節點的Redis集群:
1、命令默認生成6個集群模式的Redis實例,端口30001 ~ 30006
cd utils/create-cluster/
./create-cluster start
2、連接各個實例,創建Redis集群
cd utils/create-cluster/ ./create-cluster create
[OK] All 16384 slots covered. #表示創建成功
3、默認生成的文件都在當前目錄,包括持久化AOF文件,日志文件,RDB數據文件(手動也會生成)
[root@hd4 create-cluster]# ll
總用量 92
-rw-r--r-- 1 root root 2518 5月 31 16:27 30001.log
-rw-r--r-- 1 root root 3683 5月 31 16:27 30002.log
-rw-r--r-- 1 root root 3683 5月 31 16:27 30003.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30004.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30005.log
-rw-r--r-- 1 root root 4739 5月 31 16:27 30006.log
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30001.aof
-rw-r--r-- 1 root root 0 5月 31 16:20 appendonly-30002.aof
-rw-r--r-- 1 root root 0 5月 31 16:20 appendonly-30003.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30004.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30005.aof
-rw-r--r-- 1 root root 0 5月 31 16:27 appendonly-30006.aof
-rwxrwxr-x 1 root root 2321 2月 3 00:39 create-cluster
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30001.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30002.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30003.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30004.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30005.rdb
-rw-r--r-- 1 root root 175 5月 31 16:27 dump-30006.rdb
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30001.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30002.conf
-rw-r--r-- 1 root root 799 5月 31 16:27 nodes-30003.conf
-rw-r--r-- 1 root root 799 5月 31 16:27 nodes-30004.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30005.conf
-rw-r--r-- 1 root root 787 5月 31 16:27 nodes-30006.conf
-rw-rw-r-- 1 root root 1308 2月 3 00:39 README
4、其他
(1) 清除集群數據,會刪除生成的所有文件,包括aof文件,日志文件,rdb文件,nodes.conf文件
cd utils/create-cluster/ ./create-cluster clean
(2) 停止集群
cd utils/create-cluster/ ./create-cluster stop
(3) create-cluster其他用法:
[root@hd4 create-cluster]# create-cluster
Usage: /usr/bin/create-cluster [start|create|stop|watch|tail|clean]
start -- Launch Redis Cluster instances.
create -- Create a cluster using redis-trib create.
stop -- Stop Redis Cluster instances.
watch -- Show CLUSTER NODES output (first 30 lines) of first node.
tail <id> -- Run tail -f of instance at base port + ID.
clean -- Remove all instances data, logs, configs.
clean-logs -- Remove just instances logs.
(4) 解析create-cluster腳本,我們可以根據需求修改腳本
a. PORT=30000 #集群端口從30000開始,生成“NODES”個實例
b. TIMEOUT=2000 #指定cluster-node-timeout
c. NODES=6 #生成節點數量
d. REPLICAS=1 #表示每個master需要一個slave
玩轉Redis集群
現階段Redis集群的一個問題就是現成的客戶端庫比較少。現知道的有如下幾種:
·redis-rb-cluster Ruby實現,是redis-rb的簡單封裝,實現了與集群交互的基礎功能
·redis-py-cluster Python實現的redis-rb-cluster接口,支持大部分redis-py功能,正處於活躍開發狀態
·Predis 支持Redis集群,處於活躍狀態
·Jedis java實現,最近添加了對集群的支持
·StackExchange.Redis 提供C#支持(且對大多數.NET語言都適用,VB,F#等)
·thunk-redis 提供對Node.js和io.js的支持,它是支持pipelining和集群的thunk/promise-based客戶端
·redis-go-cluster 集群工具,使用Redisgo庫實現的go語言客戶端,通過結果聚合實現MGET/MSET
·redis-cli github中的redis不穩定分支,-c選項提供了基礎的集群管理功能
以下是redis-cli工具的檢測測試:
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
redis-cli實現了集群客戶端最基礎的功能:將客戶端請求正確重定向到正確節點。如上例中,寫入和讀取時會自動切換到不同節點。邏輯實現比較嚴謹的客戶端可以緩存哈希槽到節點的映射關系,讓客戶端直接連接到正確的節點,只有集群配置發生改變時才刷新映射關系,如故障轉移后,或者增加/刪除節點。
使用“redis-rb-cluster”編寫示例程序
在說明如何操作Redis集群前,如故障切換或重新分片,先創建一個示例程序了解Redis集群與客戶端是怎么交互的。
我們通過一個例子,讓部分節點故障或重新分片等,來了解這實際運作中,redis集群是如何處理的。如果這期間沒有客戶端對集群發起寫操作,將不益於我們了解情況
這節通過2個例子來演示redis-rb-cluster的基礎用法,下面是第一個例子,源碼在redis-rb-cluster目錄下的example.rb文件中。
1 require './cluster'
2
3 if ARGV.length != 2
4 startup_nodes = [
5 {:host => "127.0.0.1", :port => 7000},
6 {:host => "127.0.0.1", :port => 7001}
7 ]
8 else
9 startup_nodes = [
10 {:host => ARGV[0], :port => ARGV[1].to_i}
11 ]
12 end
13
14 rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)
15
16 last = false
17
18 while not last
19 begin
20 last = rc.get("__last__")
21 last = 0 if !last
22 rescue => e
23 puts "error #{e.to_s}"
24 sleep 1
25 end
26 end
27
28 ((last.to_i+1)..1000000000).each{|x|
29 begin
30 rc.set("foo#{x}",x)
31 puts rc.get("foo#{x}")
32 rc.set("__last__",x)
33 rescue => e
34 puts "error #{e.to_s}"
35 end
36 sleep 0.1
37 }
程序做的事情非常簡單: 它不斷地以foo<number>為鍵,number為值,使用SET命令向數據庫設置鍵值對:
·SET foo0 0
·SET foo1 1
·SET foo2 2
·And so forth…
代碼中的每個集群操作都使用一個begin和rescue代碼塊(block)包裹着,因為我們希望在代碼出錯時,將錯誤打印到終端上面, 而不希望應用因為異常(exception)而退出。
代碼的第七行是代碼中第一個有趣的地方,它創建了一個Redis集群對象, 其中創建對象所使用的參數及其意義如下:第一個參數是記錄了啟動節點的startup_nodes列表, 列表中包含了兩個集群節點的地址。第二個參數指定了對於集群中的各個不同的節點, Redis 集群對象可以獲得的最大連接數,第三個參數timeout指定了一個命令在執行多久之后,才會被看作是執行失敗。
啟動列表中並不需要包含所有集群節點的地址,但這些地址中至少要有一個是有效的:一旦redis-rb-cluster成功連接上集群中的某個節點時,集群節點列表就會被自動更新, 任何真正的的集群客戶端都應該這樣做。
現在,程序創建的Redis集群對象實例被保存到rc變量里面,我們可以將這個對象當作普通Redis對象實例來使用。
在十一至十九行,我們先嘗試閱讀計數器中的值,如果計數器不存在的話, 我們才將計數器初始化為0:通過將計數值保存到Redis的計數器里面,我們可以在示例重啟之后,仍然繼續之前的執行過程,而不必每次重啟之后都從foo0開始重新設置鍵值對。為了讓程序在集群下線的情況下, 仍然不斷地嘗試讀取計數器的值, 我們將讀取操作包含在了一個 while 循環里面, 一般的應用程序並不需要如此小心。
二十一至三十行是程序的主循環,這個循環負責設置鍵值對,並在設置出錯時打印錯誤信息。程序在主循環的末尾添加了一個sleep調用,讓寫操作的執行速度變慢,幫助執行示例的人更容易看清程序的輸出。執行example.rb程序將產生以下輸出:
ruby ./example.rb
1
2
3
4
5
6
7
8
9
^C (I stopped the program here)
這個程序並不是十分有趣, 稍后我們就會看到一個更有趣的集群應用示例, 不過在此之前, 讓我們先使用這個示例來演示集群的重新分片操作。
集群重新分片
現試試對集群進行重新分片操作。執行重新分片的過程中,讓example.rb程序處於運行狀態,這樣你就會看到,重新分片並不會對正在運行的集群程序產生任何影響,你也可以考慮將example.rb中的sleep調用刪掉,從而讓重新分片操作在近乎真實的寫負載下執行;重新分片操作基本上就是將某些節點上的哈希槽移動到另外一些節點上面,和創建集群一樣,重新分片也可以使用redis-trib程序來執行
執行命令可開始一次重新分片操作:
redis-trib.rb reshard 127.0.0.1:7000
指定集群中一個節點地址即可,“redis-trib”就會自動找到集群中其他節點。
目前redis-trib只能在管理員的協助下完成重新分片,要讓redis-trib自動將哈希槽移動到另一節點目前無法完成。另外,移動哈希槽目前只能以數量進行移動,不能以百分比進行移動,就是說指定redistrib移動50%哈希槽到另一節點是不行的,必須指定數字。
此命令以問題的方式開啟一次重新分片(reshard)。
第一個問題,你想重新分片多少個哈希槽:
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots: (0 slots) slave
replicates 5055b631a9b310417fa75948a5e473e2e2e1cfee
M: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 1000
上面程序一直在運行,此時已經累計不少key了,嘗試移動1000個哈希槽。
此時出現第二個問題,接收這些哈希槽的節點ID是多少:
What is the receiving node ID?a466e88499423858c5f53de9be640500d9fb3e5b
接下來“redis-trib”需知道要把這1000個哈希槽移動到哪個節點,即哪個目標節點負責接收這些哈希槽。此時我想移動到127.0.0.1:7000這個節點。指定節點要使用節點ID,且指定的節點必須是master。節點信息已經打印在屏幕。也可通過以下命令找到指定節點ID:
redis-cli -p 7000 cluster nodes |grep 127.0.0.1:7000
a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000@17000 myself,master - 0 1528103629000 1 connected 0-5460
可看到目標節點為:a466e88499423858c5f53de9be640500d9fb3e5b
接下來第三個問題,指定從哪些節點來移動keys到目標節點:
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots. #從其他每個master上取一些哈希槽移動到目標節點,選擇“all”
Type 'done' once you entered all the source nodes IDs. #指定要從哪些節點取哈希槽,輸入各節點ID,以回車鍵分隔,完成后輸入“done”指示節點指定完成
Source node #1:d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
Source node #2:5055b631a9b310417fa75948a5e473e2e2e1cfee
Source node #3:done
接下來會列出要移動的哈希槽信息,然后提示是否執行重新分片計划:
Do you want to proceed with the proposed reshard plan (yes/no)?yes
確認后你將看到redis-trib移動的哈希槽的信息,移動的key也會打印出來。
檢查一下哈希槽分布情況(平均分布的時,三個master平均能分配到16384/3個哈希槽):
redis-trib.rb check 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5961,10923-11421 (6461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
S: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots: (0 slots) slave
replicates 5055b631a9b310417fa75948a5e473e2e2e1cfee
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:11422-16383 (4962 slots) master
1 additional replica(s)
M: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots:5962-10922 (4961 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
如何編寫重新分片腳本?
如果需要經常對集群執行重新分片,可以構建一些自動功能,但目前無法redis-trib自動重新平衡集群,檢查整個集群節點上的key分布、智能地遷移哈希槽,該功能將在未來添加。
reshard也可不用交互模式執行,如以下命令行:
redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>
一個更有趣的程序
前面使用的示例程序example.rb它只是不斷地對集群進行寫入,並不檢查寫入結果是否正確。如集群可能錯誤地將example.rb發送的所有SET命令都改成了SET foo 42,但由於example.rb並不檢查寫入后的值,所以它不會意識到集群實際上寫入的值是錯誤的。因此,redis-rb-cluster 項目包含了一個名為consistency-test.rb的示例應用,這個應用比起example.rb有趣得多:它創建了多個計數器(默認1000個),並通過發送INCR命令來增加這些計數器的值。
在增加計數器值的同時,consistency-test.rb還執行以下操作:每次使用INCR命令更新一個計數器時,應用會記錄下計數器執行INCR命令之后應該有的值。比如如果計數器的起始值為0,而這次是程序第50次向它發送INCR命令,那么計數器的值應該是50。
在每次發送INCR命令之前,程序會隨機從集群中讀取一個計數器的值,並將它與自己記錄的值進行對比,看兩個值是否相同。
也就是說,這個程序是一個一致性檢查器(consistency checker):若集群在執行INCR命令的過程中,丟失了某條INCR命令,又或者多執行了某條客戶端沒有確認到的INCR命令,那么檢查器將察覺到這一點:在前一種情況中,consistency-test.rb記錄的計數器值將比集群記錄的計數器值要大;而在后一種情況中,consistency-test.rb記錄的計數器值將比集群記錄的計數器值要小。
運行consistency-test程序將產生類似以下的輸出:
$ ruby consistency-test.rb
925 R (0 err) | 925 W (0 err) |
5030 R (0 err) | 5030 W (0 err) |
9261 R (0 err) | 9261 W (0 err) |
13517 R (0 err) | 13517 W (0 err) |
17780 R (0 err) | 17780 W (0 err) |
22025 R (0 err) | 22025 W (0 err) |
25818 R (0 err) | 25818 W (0 err) |
結果展示了執行的讀寫和錯誤(由於系統不可用而沒有接受的查詢發生的錯誤)的數量.
若程序察覺了不一致的情況出現,它將在輸出行的末尾顯式不一致的詳細情況。比如,如果我們在consistency-test.rb運行的過程中,手動修改某個計數器的值:
$ redis 127.0.0.1:7000> set key_217 0
OK
(in the other tab I see...)
94774 R (0 err) | 94774 W (0 err) |
98821 R (0 err) | 98821 W (0 err) |
102886 R (0 err) | 102886 W (0 err) | 114 lost |
107046 R (0 err) | 107046 W (0 err) | 114 lost |
在我們修改計數器值的時候,計數器的正確值是114(執行了114次INCR命令),因為我們將計數器的值設成了0, 所以consistency-test.rb會向我們報告說丟失了114個INCR命令。
這個程序作為測試程序很有意思,所以我們用這個程序來測試故障恢復
測試故障轉移
在執行本節操作的過程中,請一直運行consistency-test程序。要觸發一次故障轉移,最簡單的辦法就是令集群中的某個master節點進入下線狀態。首先用以下命令列出集群中的所有master節點:
redis-cli -p 7000 cluster nodes | grep master
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
通過命令輸出得知端口號為7000、7001和7002的節點都是master節點, 然后我們可以通過向端口號7002的master節點發送DEBUG SEGFAULT命令,讓這個master節點崩潰:
redis-cli -p 7002 debug segfault
Error: Server closed the connection
現在,切換到運行着consistency-test的標簽頁,可以看到consistency-test在7002下線之后的一段時間里將產生大量的錯誤警告信息:
18849 R (0 err) | 18849 W (0 err) |
23151 R (0 err) | 23151 W (0 err) |
27302 R (0 err) | 27302 W (0 err) |
... many error warnings here ...
29659 R (578 err) | 29660 W (577 err) |
33749 R (578 err) | 33750 W (577 err) |
37918 R (578 err) | 37919 W (577 err) |
42077 R (578 err) | 42078 W (577 err) |
從consistency-test的這段輸出可以看到,集群在執行故障轉移期間,總共丟失了578個讀命令和577個寫命令,但是並沒有產生任何數據不一致。這聽上去可能有點奇怪,因為在教程的開頭我們提到過Redis使用的是異步復制,在執行故障轉移期間,集群可能會丟失寫命令。但是在實際上,丟失命令的情況並不常見,因為Redis幾乎是同時執行將命令回復發送給客戶端,以及將命令復制給slave節點這兩個操作,所以實際上造成命令丟失的時間窗口是非常小的。不過,盡管出現的幾率不高,但丟失命令的情況還是有可能會出現的,所以我們對Redis集群不能提供強一致性的這一描述仍然是正確的。現在, 讓我們使用cluster nodes命令,查看集群在執行故障轉移操作之后,主slave節點的布局情況:
redis-cli -p 7000 cluster nodes
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected
現在masters運行在 7000, 7001 和 7005端口上. 原來的master 7002現在變成了一個7005的一個slave節點.
CLUSTER NODES命令的輸出看起來有點復雜,其實他非常的簡單,含義如下:
·節點ID
·IP:端口
·標志: master, slave, myself, fail, …
·如果是個slave節點, 這里是它的master節點的NODE ID
·集群最近一次向節點發送 PING 命令之后, 過去了多長時間還沒接到回復。.
·節點最近一次返回 PONG 回復的時間。
·節點的配置版本號(configuration epoch):詳細信息請參考Redis集群規范 。
·本節點的網絡連接情況:例如 connected 。
·節點目前包含的槽:例如 127.0.0.1:7001 目前包含號碼為5960至10921的哈希槽。
手動故障轉移
有時在master沒有任何問題時強制故障轉移是很有必要的,如想升級master的Redis進程,一般會先故障轉移,將其降級為slave再進行升級操作,以此降低對整個集群的影響。
Redis集群使用“CLUSTER FAILOVER”來手動執行故障轉移,該命令必須在要故障轉移的master的一個slave上執行。
相較於真實的master故障然后故障轉移,手動故障轉移是比較安全的,因為手動故障轉移時客戶端的切換是在確保新的master完全復制了故障的舊的master數據的前提下發生的,所以避免了數據的丟失。
手動執行故障轉移時,slave日志如下:
# Manual failover user request accepted.
# Received replication offset for paused master manual failover: 347540
# All master replication stream processed, manual failover can start.
# Start of election delayed for 0 milliseconds (rank #0, offset 347540).
# Starting a failover election for epoch 7545.
# Failover election won: I'm the new master.
其基本過程如:客戶端不再連接舊的master,已經連接了的會被鎖住,同時舊master向slave發送復制偏移量,slave得到偏移量后開始故障轉移,然后新master通知舊master更新配置,成為新master的slave,此時還連接在舊master上的客戶端解鎖后會被重定向到新master。
添加一個新節點
基本過程是:添加一個空節點,然后移動一些數據給他。有兩種情況:若新增的是master,從集群的其他節點中轉移部分數據給它;若新增的是slave,則告訴它從一個已知的節點中同步復制集。
添加master節點的情況
1、添加一個空的redis實例節點,此處添加一個運行在端口7006的實例
2、將空實例添加到現有集群中:
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
>>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster.
[OK] New node added correctly.
可以看到.使用addnode命令來添加節點,第一個參數是新節點的地址,第二個參數是任意一個已經存在的節點的IP和端口
redis-trib腳本只是給發送“CLUSTER MEET”消息給節點,這也可以手動地通過客戶端發送,但redis-trib在發送之前會檢查集群的狀態,所以,還是用redis-trib腳本來操作集群會比較好。
3、查看集群狀態:
redis-cli -c -p 7006 cluster nodes
406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005@17005 slave a466e88499423858c5f53de9be640500d9fb3e5b 0 1528264952479 7 connected
bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006@17006 myself,master - 0 1528264951000 0 connected
5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001@17001 slave 9b1d9c3e7bbcc955afce649f439cd2d094957313 0 1528264951000 8 connected
9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003@17003 master - 0 1528264950000 8 connected 5962-10922
a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000@17000 master - 0 1528264951578 7 connected 0-5961 10923-11421
b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004@17004 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528264952078 3 connected
d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002@17002 master - 0 1528264951477 3 connected 11422-16383
redis-trib.rb check 127.0.0.1:7006
>>> Performing Cluster Check (using node 127.0.0.1:7006)
M: bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006
slots: (0 slots) master
0 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
S: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots: (0 slots) slave
replicates 9b1d9c3e7bbcc955afce649f439cd2d094957313
M: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots:5962-10922 (4961 slots) master
1 additional replica(s)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:0-5961,10923-11421 (6461 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:11422-16383 (4962 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
與其他master相比,新節點有兩點區別:
·新節點未包含任何數據,因為哈希槽不是自動分配的,需要手工分配到新節點
·由於新節點還未包含哈希槽,當集群中某個slave想提成為master時,他不會參與選舉過程
4、重新分配哈希槽,然后檢查哈希槽分布:
redis-trib.rb reshard 127.0.0.1:7006 redis-trib.rb check 127.0.0.1:7006
>>> Performing Cluster Check (using node 127.0.0.1:7006)
M: bde6fc14465ecdbc71c6630edb5f9a3ab0c45cf0 127.0.0.1:7006
slots:0-1615,5962-7201,11422-12661 (4096 slots) master
0 additional replica(s)
S: 406bda57ed591c2bd3b15955f687a57b03a653c0 127.0.0.1:7005
slots: (0 slots) slave
replicates a466e88499423858c5f53de9be640500d9fb3e5b
S: 5055b631a9b310417fa75948a5e473e2e2e1cfee 127.0.0.1:7001
slots: (0 slots) slave
replicates 9b1d9c3e7bbcc955afce649f439cd2d094957313
M: 9b1d9c3e7bbcc955afce649f439cd2d094957313 127.0.0.1:7003
slots:1616-1990,7202-10922 (4096 slots) master
1 additional replica(s)
M: a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7000
slots:2365-5961,10923-11421 (4096 slots) master
1 additional replica(s)
S: b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004
slots: (0 slots) slave
replicates d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
M: d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 127.0.0.1:7002
slots:1991-2364,12662-16383 (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
添加slave副本節點的情況
添加一個slave副本節點有兩種方法。
不管使用哪種方法都得先啟動一個空Redis實例節點。
第一種方法:啟動一個Redis空實例,后使用 redis-trib腳本的“--slave”選項:
redis-trib.rb add-node --slave 127.0.0.1:7007 127.0.0.1:7000
第一個參數是新slave節點的地址;
第二個參數是任意一個已經存在的節點的IP和端口,不需要指定此slave要添加為哪個master副本節點,redis-trib會在擁有最少slave節點的master節點中隨機選一個作為新增節點的master節點
輸出信息:
...
Automatically selected master 127.0.0.1:7006
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.
...
Configure node as replica of 127.0.0.1:7006.
...
也可以直接指定新增slave節點的master節點:
redis-trib.rb add-node --slave --master-id a466e88499423858c5f53de9be640500d9fb3e5b 127.0.0.1:7008 127.0.0.1:7003
如此,便指定了新的slave節點是哪個master節點的副本集。
第二種方法:啟動一個Redis空實例,后將新節點以一個空master節點的形式加入到集群,然后再用“CLUSTER REPLICATE”將其轉換為slave節點。此方法也適用於給從slave節點更換master節點。
如,集群中master節點127.0.0.1:7002承載的哈希槽范圍是1991-2364,12662-16383,節點ID是d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f
1、將空實例添加到現有集群中:
redis-trib.rb add-node 127.0.0.1:7009 127.0.0.1:7000
2、連接空實例,執行復制進程:
redis-cli -p 7009 CLUSTER REPLICATE d1ce7d9db6086c41f13ef0ce3753f82a3bfc
3、認證信息:
redis-cli -p 7000 cluster nodes |grep slave |grep d1ce7d9db6086c41f13ef0ce3753f82a3bfc
b36883be3b39692f71a441a67277ab23dff80afb 127.0.0.1:7004@17004 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528271555000 11 connected
5837a7c77a04b5100222dca1d226e4980764a97f 127.0.0.1:7009@17009 slave d1ce7d9db6086c41f13ef0ce3753f82a3bfc420f 0 1528271555500 11 connected
可看到127.0.0.1:7004和127.0.0.1:7009都是127.0.0.1:7002的slave復制節點。
刪除節點
使用“del-node”指令刪除slave節點:
redis-trib.rb del-node 127.0.0.1:7000 `<node-id>`
第一個參數是任意一個節點的地址
第二個節點是你想要移除的節點ID
使用“del-node”指令刪除master節點:
1、必須先將哈希槽全部遷移至別的master節點
2、在使用“del-node”指令刪除master節點
另外的一個方法是先將master故障轉移降級為slave,且將另外的slave提升為master后再刪除節點。顯然,這樣並沒有減少master節點數量,最終還是只能先遷移哈希槽然后執行刪除動作
Slave副本節點的遷移(Replicas migration)
Redis集群中給slaveslave節點更換master節點只需執行指令:
CLUSTER REPLICATE <master-node-id>
在特定場景下,無需管理員協助,自動將一個slave節點從當前master節點切換到另一個master節點的自動重新配置過程叫副本遷移(Replicas migration)。
注意;更多副本遷移的詳細介紹在Redis集群規范中,此處只做簡單介紹
在某些情況下,將slave節點遷移到另一個master節點下的原因通常是the Redis Cluster is as resistant to failures as the number of replicas attached to a given master
例如如果master節點和slave復制節點同時故障,那么一個master節點僅有一個slave復制節點的集群將不能再正常運行,因為沒有其他Redis實例擁有故障master和slave節點所承載的哈希槽。雖然網絡分區會造成一些節點被隔離,但其他情況也會導致集群故障,如硬件或者軟件的故障導致節點宕機,此情況一般不會所有節點同時故障。再比如,集群中每個master節點都有一個slave節點,凌晨4點時一個slave節點被kill掉,然后master節點在凌晨6點被kill掉,這樣依然會導致集群不能工作。
為提高系統可用性,可為每個master添加額外的slave復制節點,但會增加成本。副本遷移功能可以只為部分master節點增加更多的slave復制節點。如集群中有10個master節點,每個有一個slave復制節點,共20個實例,然后可以將一些master的slave增加至3個。
當集群中存在一個master節點有多個slave節點,但另一個master節點沒有slave節點時,復制集遷移功能會在master的多個slave節點中找一個節點,給沒有slave節點的master節點做復制集。如當凌晨4點一個slave節點掛掉,另一個slave節點將會代替它成為該master節點的slave節點,然后如果凌晨5點master節點掛掉還能有一個slave節點可以升級為master,此時集群仍可以正常運行。
因此副本遷移功能總結為:
·為了讓副本遷移功能生效,需要在集群任意master節點上多添加幾個slave節點
·在必要時,集群找到擁有最多slave節點的master,從其中挑選一個slave,進行副本遷移
·redis.conf中“cluster-migration-barrier”參數用於配置副本遷移。此參數保證Redis集群中不會出現裸奔的master節點,即保證每個master節點都有slave節點。只有當一個master節點至少擁有給定數量個處於正常工作中的slave節點時,才會分配slave節點給集群中孤立的master節點。這個給定數量就是 cluster-migration-barrier。給定數量是1意味着一個slave節點只有在其master節點另外至少還有一個正常工作的slave節點的情況下才會被分配(副本遷移)。那些分配后仍然剩余migration barrier個slave節點的master節點才會觸發副本遷移,而不是分配前有migration barrier個slave節點的master節點會觸發節點分配!!
升級Redis集群中的節點版本
升級slave節點版本
只需要停止slave節點並使用新版本的Redis重新啟動在相同端口即可。在節點關閉期間客戶端連接過來會被重定向到另外可用的slave節點上。
升級master節點版本
相對復雜,建議的流程是:
1、使用指令“CLUSTER FAILOVER”手動觸發故障轉移,使master節點變成slave節點
2、按照升級slave節點的方式升級該節點
3、手動觸發故障轉移,讓它變成新的master節點
將Redis數據遷移到Redis集群
一般,數據遷移發生在兩種情況下:
·原來的數據存在於一個Redis單實例
·原來的數據存在於一個分片環境中,數據存儲在多個節點
兩種情況遷移都很方便,重要的細節是程序是否使用多鍵(multiple keys)操作,以及怎樣使用多鍵操作。有以下三種情況:
·沒有操作多個key(包括操作多個key的指令、事務、lua腳本)。所有key都獨立操作
·操作了多個key(包括操作多個key的指令、事務、lua腳本),但這些key都有相同的哈希標簽,比如這些被同時操作的key:SUNION{user:1000}.foo {user:1000}.bar
·操作了多個key(包括操作多個key的指令、事務、lua腳本),這些key沒有特別處理,也沒有相同標簽。
第三種情況redis集群無法處理,需修改程序:不要使用多個key,或者給這些key加上相同的哈希標簽,然后就可以遷移啦!
前面兩種情況可以直接做遷移,且遷移的流程一樣。
假設數據現被切割並保存在N個master節點存儲(N=1時就是沒有分片的情況),則須以以下步驟將數據遷移至Redis集群
1、停止所有連接Redis集群的客戶端程序。如果有自動在線遷移的方案,也許就不需要停止了。
2、使用指令“BGREWRITEAOF”讓所有master節點產生AOF文件,等待文件創建完成
3、修改AOF文件名稱為aof-1, aof-2...aof-N以區別
4、創建N個master節點,且開啟實例的appendonly選項。Slave復制節點可晚些再添加
5、停止新Redis集群所有節點,將前面保存的aof-1, aof-2...aof-N,aof-1給第一個節點,aof-2給第二個節點,以此類推
6、重啟所有節點,可能會有警告提示說依據配置部分key不應當存儲在此節點
7、使用指令“redis-trib.rb fix”讓集群依據哈希槽自動遷移數據
8、使用指令“redis-trib.rb check”檢查集群狀態是否正常
9、重啟客戶端程序,檢測數據讀寫
除此之外還可使用指令“redis-trib.rb import”從外部實例中導入數據到redis集群。該命令將源實例中的數據移動到目標Redis集群(該指令會把源實例中的數據都刪除)。若源實例版本是Redis2.8,導入過程可能會比較長,因為2.8版本還未實現數據遷移的連接緩存,所以建議把源實例Redis版本先升級到3.x,再進行遷移。
