redis集羣搭建

Redis 集羣教程
本文檔是Redis集羣的通常介紹,沒有涉及複雜難懂的分佈式概念的贅述,只是提供了從用戶角度來如何搭建測試以及使用的方法,若是你打算使用並深刻了解Redis集羣,推薦閱讀完本章節後,仔細閱讀 Redis 集羣規範 一章。
本教程試圖提供最終用戶一個簡單的關於集羣和一致性特徵的描述
請注意,本教程使用於Redis3.0(包括3.0)以上版本
若是你計劃部署集羣,那麼咱們建議你從閱讀這個文檔開始。
Redis集羣介紹
Redis 集羣是一個提供在多個Redis間節點間共享數據的程序集。
Redis集羣並不支持處理多個keys的命令,由於這須要在不一樣的節點間移動數據,從而達不到像Redis那樣的性能,在高負載的狀況下可能會致使不可預料的錯誤.
Redis 集羣經過分區來提供必定程度的可用性,在實際環境中當某個節點宕機或者不可達的狀況下繼續處理命令.Redis 集羣的優點:
自動分割數據到不一樣的節點上。
整個集羣的部分節點失敗或者不可達的狀況下可以繼續處理命令。
Redis 集羣的數據分片
Redis 集羣沒有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集羣有16384個哈希槽,每一個key經過CRC16校驗後對16384取模來決定放置哪一個槽.集羣的每一個節點負責一部分hash槽,舉個例子,好比當前集羣有3個節點,那麼:
節點 A 包含 0 到 5500號哈希槽.
節點 B 包含5501 到 11000 號哈希槽.
節點 C 包含11001 到 16384號哈希槽.
這種結構很容易添加或者刪除節點. 好比若是我想新添加個節點D, 我須要從節點 A, B, C中得部分槽到D上. 若是我像移除節點A,須要將A中得槽移到B和C節點上,而後將沒有任何槽的A節點從集羣中移除便可.因爲從一個節點將哈希槽移動到另外一個節點並不會中止服務,因此不管添加刪除或者改變某個節點的哈希槽的數量都不會形成集羣不可用的狀態.
Redis 集羣的主從複製模型
爲了使在部分節點失敗或者大部分節點沒法通訊的狀況下集羣仍然可用,因此集羣使用了主從複製模型,每一個節點都會有N-1個複製品.
在咱們例子中具備A,B,C三個節點的集羣,在沒有複製模型的狀況下,若是節點B失敗了,那麼整個集羣就會覺得缺乏5501-11000這個範圍的槽而不可用.
然而若是在集羣建立的時候(或者過一段時間)咱們爲每一個節點添加一個從節點A1,B1,C1,那麼整個集羣便有三個master節點和三個slave節點組成,這樣在節點B失敗後,集羣便會選舉B1爲新的主節點繼續服務,整個集羣便不會由於槽找不到而不可用了
不過當B和B1 都失敗後,集羣是不可用的.
Redis 一致性保證
Redis 並不能保證數據的強一致性. 這意味這在實際中集羣在特定的條件下可能會丟失寫操做.
第一個緣由是由於集羣是用了異步複製. 寫操做過程:
客戶端向主節點B寫入一條命令.
主節點B向客戶端回覆命令狀態.
主節點將寫操做複製給他得從節點 B1, B2 和 B3.
主節點對命令的複製工做發生在返回命令回覆以後, 由於若是每次處理命令請求都須要等待複製操做完成的話, 那麼主節點處理命令請求的速度將極大地下降 —— 咱們必須在性能和一致性之間作出權衡。注意:Redis 集羣可能會在未來提供同步寫的方法。Redis 集羣另一種可能會丟失命令的狀況是集羣出現了網絡分區, 而且一個客戶端與至少包括一個主節點在內的少數實例被孤立。
舉個例子 假設集羣包含 A 、 B 、 C 、 A1 、 B1 、 C1 六個節點, 其中 A 、B 、C 爲主節點, A1 、B1 、C1 爲A,B,C的從節點, 還有一個客戶端 Z1假設集羣中發生網絡分區,那麼集羣可能會分爲兩方,大部分的一方包含節點 A 、C 、A1 、B1 和 C1 ,小部分的一方則包含節點 B 和客戶端 Z1 .
Z1仍然可以向主節點B中寫入, 若是網絡分區發生時間較短,那麼集羣將會繼續正常運做,若是分區的時間足夠讓大部分的一方將B1選舉爲新的master,那麼Z1寫入B中得數據便丟失了.
注意, 在網絡分裂出現期間, 客戶端 Z1 能夠向主節點 B 發送寫命令的最大時間是有限制的, 這一時間限制稱爲節點超時時間(node timeout), 是 Redis 集羣的一個重要的配置選項:
搭建並使用Redis集羣
搭建集羣的第一件事情咱們須要一些運行在 集羣模式的Redis實例. 這意味這集羣並非由一些普通的Redis實例組成的,集羣模式須要經過配置啓用,開啓集羣模式後的Redis實例即可以使用集羣特有的命令和特性了.
下面是一個最少選項的集羣的配置文件:java

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yesnode

文件中的 cluster-enabled 選項用於開實例的集羣模式, 而 cluster-conf-file 選項則設定了保存節點配置文件的路徑, 默認值爲 nodes.conf.節點配置文件無須人爲修改, 它由 Redis 集羣在啓動時建立, 並在有須要時自動進行更新。
要讓集羣正常運做至少須要三個主節點,不過在剛開始試用集羣功能時, 強烈建議使用六個節點: 其中三個爲主節點, 而其他三個則是各個主節點的從節點。
首先, 讓咱們進入一個新目錄, 並建立六個以端口號爲名字的子目錄, 稍後咱們在將每一個目錄中運行一個 Redis 實例:命令以下:git

mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005github

在文件夾 7000 至 7005 中, 各建立一個 redis.conf 文件, 文件的內容可使用上面的示例配置文件, 但記得將配置中的端口號從 7000 改成與文件夾名字相同的號碼。
從 Redis Github 頁面 的 unstable 分支中取出最新的 Redis 源碼, 編譯出可執行文件 redis-server , 並將文件複製到 cluster-test 文件夾, 而後使用相似如下命令, 在每一個標籤頁中打開一個實例:redis

cd 7000
../redis-server ./redis.conf數據庫

實例打印的日誌顯示, 由於 nodes.conf 文件不存在, 因此每一個節點都爲它自身指定了一個新的 ID :vim

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1api

實例會一直使用同一個 ID , 從而在集羣中保持一個獨一無二(unique)的名字。
搭建集羣
如今咱們已經有了六個正在運行中的 Redis 實例, 接下來咱們須要使用這些實例來建立集羣, 併爲每一個節點編寫配置文件。
經過使用 Redis 集羣命令行工具 redis-trib , 編寫節點配置文件的工做能夠很是容易地完成: redis-trib 位於 Redis 源碼的 src 文件夾中, 它是一個 Ruby 程序, 這個程序經過向實例發送特殊命令來完成建立新集羣, 檢查集羣, 或者對集羣進行從新分片(reshared)等工做。緩存

./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 表示咱們但願爲集羣中的每一個主節點建立一個從節點。
以後跟着的其餘參數則是這個集羣實例的地址列表,3個master3個slaveredis-trib 會打印出一份預想中的配置給你看, 若是你以爲沒問題的話, 就能夠輸入 yes , redis-trib 就會將這份配置應用到集羣當中,讓各個節點開始互相通信,最後能夠獲得以下信息:

[OK] All 16384 slots covered

這表示集羣中的 16384 個槽都有至少一個主節點在處理, 集羣運做正常。
Creating a Redis Cluster using the create-cluster script
If you don’t want to create a Redis Cluster by configuring and executingindividual instances manually as explained above, there is a much simplersystem (but you’ll not learn the same amount of operational details).
Just check utils/create-cluster directory in the Redis distribution.There is a script called create-cluster inside (same name as the directoryit is contained into), it’s a simple bash script. In order to starta 6 nodes cluster with 3 masters and 3 slaves just type the followingcommands:
create-cluster start
create-cluster create
Reply to yes in step 2 when the redis-trib utility wants you to acceptthe cluster layout.
You can now interact with the cluster, the first node will start at port 30001by default. When you are done, stop the cluster with:
create-cluster stop.
Please read the README inside this directory for more information on howto run the script.
使用集羣
Redis 集羣現階段的一個問題是客戶端實現不多。
如下是一些我知道的實現:
redis-rb-cluster 是我(@antirez)編寫的 Ruby 實現, 用於做爲其餘實現的參考。 該實現是對 redis-rb 的一個簡單包裝, 高效地實現了與集羣進行通信所需的最少語義(semantic).
redis-py-cluster 看上去是 redis-rb-cluster 的一個 Python 版本, 這個項目有一段時間沒有更新了(最後一次提交是在六個月以前), 不過能夠將這個項目用做學習集羣的起點。
流行的 Predis 曾經對早期的 Redis 集羣有過必定的支持, 但我不肯定它對集羣的支持是否完整, 也不清楚它是否和最新版本的 Redis 集羣兼容 (由於新版的 Redis 集羣將槽的數量從 4k 改成 16k 了).
使用最多的時java客戶端, Jedis 最近添加了對集羣的支持, 詳細請查看項目README中Jedis Cluster部分.
StackExchange.Redis 提供對 C# 的支持(而且包括大部分 .NET 下面的語言,好比: VB, F#等等)
thunk-redis 提供對 Node.js 和 io.js的支持。
Redis unstable 分支中的 redis-cli 程序實現了很是基本的集羣支持, 可使用命令 redis-cli -c 來啓動。
測試 Redis 集羣比較簡單的辦法就是使用 redis-rb-cluster 或者 redis-cli , 接下來咱們將使用 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"

注意: 若是你是使用腳本建立的集羣節點,那麼默認端口多是從30001開始。
redis-cli 對集羣的支持是很是基本的, 因此它老是依靠 Redis 集羣節點來將它轉向(redirect)至正確的節點。一個真正的(serious)集羣客戶端應該作得比這更好: 它應該用緩存記錄起哈希槽與節點地址之間的映射(map), 從而直接將命令發送到正確的節點上面。這種映射只會在集羣的配置出現某些修改時變化, 好比說, 在一次故障轉移(failover)以後, 或者系統管理員經過添加節點或移除節點來修改了集羣的佈局(layout)以後, 諸如此類。
使用redis-rb-cluster寫一個例子
在展現如何使用集羣進行故障轉移、從新分片等操做以前, 咱們須要建立一個示例應用, 瞭解一些與 Redis 集羣客戶端進行交互的基本方法。
在運行示例應用的過程當中, 咱們會嘗試讓節點進入失效狀態, 又或者開始一次從新分片, 以此來觀察 Redis 集羣在真實世界運行時的表現, 而且爲了讓這個示例儘量地有用, 咱們會讓這個應用向集羣進行寫操做。
本節將經過兩個示例應用來展現 redis-rb-cluster 的基本用法, 如下是本節的第一個示例應用, 它是一個名爲 example.rb 的文件, 包含在redis-rb-cluster 項目裏面

1 require './cluster'
2
3 startup_nodes = [
4 {:host => "127.0.0.1", :port => 7000},
5 {:host => "127.0.0.1", :port => 7001}
6 ]
7 rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)
8
9 last = false
10
11 while not last
12 begin
13 last = rc.get("last")
14 last = 0 if !last
15 rescue => e
16 puts "error #{e.to_s}"
17 sleep 1
18 end
19 end
20
21 ((last.to_i+1)..1000000000).each{|x|
22 begin
23 rc.set("foo#{x}",x)
24 puts rc.get("foo#{x}")
25 rc.set("last",x)
26 rescue => e
27 puts "error #{e.to_s}"
28 end
29 sleep 0.1
30 }

這個應用所作的工做很是簡單: 它不斷地以 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 自動將哈希槽從一個節點移動到另外一個節點, 目前來講還作不到
你想移動多少個槽( 從1 到 16384)?
咱們嘗試從將100個槽從新分片, 若是 example.rb 程序一直運行着的話, 如今 1000 個槽裏面應該有很多鍵了。
除了移動的哈希槽數量以外, redis-trib 還須要知道從新分片的目標, 也便是, 負責接收這 1000 個哈希槽的節點。

$ redis-cli -p 7000 cluster nodes | grep myself
97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460

個人目標節點是 97a3a64667477371c4479320d683e4c8db5858b1.
如今須要指定從哪些節點來移動keys到目標節點 我輸入的是all ,這樣就會從其餘每一個master上取一些哈希槽。
最後確認後你將會看到每一個redis-trib移動的槽的信息,每一個key的移動的信息也會打印出來在從新分片的過程當中,你的例子程序是不會受到影響的,你能夠中止或者從新啓動屢次。
在從新分片結束後你能夠經過以下命令檢查集羣狀態:

./redis-trib.rb check 127.0.0.1:7000

一個更有趣的程序
咱們在前面使用的示例程序 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 程序。要觸發一次故障轉移, 最簡單的辦法就是令集羣中的某個主節點進入下線狀態。首先用如下命令列出集羣中的全部主節點:

$ 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 的節點都是主節點, 而後咱們能夠經過向端口號爲7002 的主節點發送 DEBUG SEGFAULT 命令, 讓這個主節點崩潰:

$ 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 幾乎是同時執行將命令回覆發送給客戶端, 以及將命令複製給從節點這兩個操做, 因此實際上形成命令丟失的時間窗口是很是小的。不過, 儘管出現的概率不高, 但丟失命令的狀況仍是有可能會出現的, 因此咱們對 Redis 集羣不能提供強一致性的這一描述仍然是正確的。如今, 讓咱們使用 cluster nodes 命令,查看集羣在執行故障轉移操做以後, 主從節點的佈局狀況:

$ 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的一個從節點.
CLUSTER NODES 命令的輸出看起來有點複雜,其實他很是的簡單,含義以下:
節點ID
IP:端口
標誌: master, slave, myself, fail, …
若是是個從節點, 這裏是它的主節點的NODE ID
集羣最近一次向節點發送 PING 命令以後, 過去了多長時間還沒接到回覆。.
節點最近一次返回 PONG 回覆的時間。
節點的配置紀元(configuration epoch):詳細信息請參考 Redis 集羣規範 。
本節點的網絡鏈接狀況:例如 connected 。
節點目前包含的槽:例如 127.0.0.1:7001 目前包含號碼爲 5960 至 10921 的哈希槽。
手動故障轉移
有的時候在主節點沒有任何問題的狀況下強制手動故障轉移也是頗有必要的,好比想要升級主節點的Redis進程,咱們能夠經過故障轉移將其轉爲slave再進行升級操做來避免對集羣的可用性形成很大的影響。
Redis集羣使用 CLUSTER FAILOVER命令來進行故障轉移,不過要被轉移的主節點的從節點上執行該命令手動故障轉移比主節點失敗自動故障轉移更加安全,由於手動故障轉移時客戶端的切換是在確保新的主節點徹底複製了失敗的舊的主節點數據的前提下下發生的,因此避免了數據的丟失。
執行手動故障轉移時從節點日誌以下:

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上解鎖後從新鏈接到新的主節點上。
添加一個新節點
添加新的節點的基本過程就是添加一個空的節點而後移動一些數據給它,有兩種狀況,添加一個主節點和添加一個從節點(添加從節點時須要將這個新的節點設置爲集羣中某個節點的複製)
針對這兩種狀況,本節都會介紹,先從添加主節點開始.
兩種狀況第一步都是要添加 一個空的節點.
啓動新的7006節點,使用的配置文件和之前的同樣,只要把端口號改一下便可,過程以下:
在終端打開一個新的標籤頁.
進入cluster-test 目錄.
建立並進入 7006文件夾.
和其餘節點同樣,建立redis.conf文件,須要將端口號改爲7006.
最後啓動節點 ../redis-server ./redis.conf
若是正常的話,節點會正確的啓動.
接下來使用redis-trib 來添加這個節點到現有的集羣中去.

./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000

能夠看到.使用addnode命令來添加節點,第一個參數是新節點的地址,第二個參數是任意一個已經存在的節點的IP和端口.咱們能夠看到新的節點已經添加到集羣中:

redis 127.0.0.1:7006> cluster nodes
3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921
3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected
f093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected
a211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected
97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422
3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383

新節點如今已經鏈接上了集羣, 成爲集羣的一份子, 而且能夠對客戶端的命令請求進行轉向了, 可是和其餘主節點相比, 新節點還有兩點區別:
新節點沒有包含任何數據, 由於它沒有包含任何哈希槽.
儘管新節點沒有包含任何哈希槽, 但它仍然是一個主節點, 因此在集羣須要將某個從節點升級爲新的主節點時, 這個新節點不會被選中。
接下來, 只要使用 redis-trib 程序, 將集羣中的某些哈希桶移動到新節點裏面, 新節點就會成爲真正的主節點了。
添加一個從節點
有兩種方法添加從節點,能夠像添加主節點同樣使用redis-trib 命令,也能夠像下面的例子同樣使用 –slave選項:

./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000

此處的命令和添加一個主節點命令相似,此處並無指定添加的這個從節點的主節點,這種狀況下系統會在其餘的複製集中的主節點中隨機選取一個做爲這個從節點的主節點。
你能夠經過下面的命令指定主節點:

./redis-trib.rb add-node --slave --master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7006 127.0.0.1:7000

也可使用CLUSTER REPLICATE 命令添加.這個命令也能夠改變一個從節點的主節點。
例如,要給主節點 127.0.0.1:7005添加一個從節點,該節點哈希槽的範圍1423-16383, 節點 ID 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e,咱們須要連接新的節點(已是空的主節點)並執行命令:

redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e

咱們新的從節點有了一些哈希槽,其餘的節點也知道(過幾秒後會更新他們本身的配置),可使用以下命令確認:

$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
f093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected
2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected

節點 3c3a0c… 有兩個從節點, 7002 (已經存在的) 和 7006 (新添加的).
移除一個節點
只要使用 del-node 命令便可:

./redis-trib del-node 127.0.0.1:7000 &lt;node-id&gt;

第一個參數是任意一個節點的地址,第二個節點是你想要移除的節點地址。
使用一樣的方法移除主節點,不過在移除主節點前,須要確保這個主節點是空的. 若是不是空的,須要將這個節點的數據從新分片到其餘主節點上.
替代移除主節點的方法是手動執行故障恢復,被移除的主節點會做爲一個從節點存在,不過這種狀況下不會減小集羣節點的數量,也須要從新分片數據.
從節點的遷移
在Redis集羣中會存在改變一個從節點的主節點的狀況,須要執行以下命令 :

CLUSTER REPLICATE <master-node-id>

在特定的場景下,不須要系統管理員的協助下,自動將一個從節點從當前的主節點切換到另外一個主節 的自動從新配置的過程叫作複製遷移(從節點遷移),從節點的遷移可以提升整個Redis集羣的可用性.
你能夠閱讀(Redis集羣規範)/topics/cluster-spec瞭解細節.
簡短的概況一下從節點遷移
集羣會在有從節點數量最多的主節點上進行從節點的遷移.
要在一個主節點上添加多個從節點.
參數來控制從節點遷移 replica-migration-barrier:你能夠仔細閱讀redis.conf 。

問題和解決方法
./redis-trib.rb create --replicas 1 192.168.1.186:6380 192.168.1.186:6381 192.168.1.186:6382 192.168.1.186:6383 192.168.1.186:6384 192.168.1.186:6385
若是報錯:
/usr/bin/env: ruby: No such file or directory
說明ruby沒有安裝
安裝ruby
yum install ruby
若是報錯:
no such file to load -- rubygems (LoadError)
安裝rubygems
yum install rubygems
執行gem install redis來把gem和redis對接
若是報錯:
redis required ruby version >= 2.2.2
升級ruby的版本信息
ruby的升級須要兩個依賴 curl和RVM
安裝culr
yum install curl
RVM
curl -L get.rvm.io | bash -s stable
若是出現如下錯誤:
Warning, RVM 1.26.0 introduces signed releases and automated check of signatures when GPG software found. Assuming you trust Michal Papis import the mpapis public key (downloading the signatures).
GPG signature verification failed for ‘/usr/local/rvm/archives/rvm-1.29.3.tgz‘ -       ‘https://github.com/rvm/rvm/releases/download/1.29.3/1.29.3.tar.gz.asc‘! Try to install GPG v2 and then fetch the public key:
gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 or if it fails:
 command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -the key can be compared with:
https://rvm.io/mpapis.asc
https://keybase.io/mpapis
NOTE: GPG version 2.1.17 have a bug which cause failures during fetching keys from remote server. Please downgrade or upgrade to newer version (if available) or use the second method described above.
解決以上錯誤     
使用curl -sSL https://rvm.io/mpapis.asc | gpg2 --import - 先生成密鑰
gpg: 鑰匙環‘/root/.gnupg/secring.gpg’已創建
gpg: /root/.gnupg/trustdb.gpg:創建了信任度數據庫
gpg: 密鑰 D39DC0E3:公鑰「Michal Papis (RVM signing) <mpapis@gmail.com>」已導入
gpg: 合計被處理的數量:1
gpg: 已導入:1 (RSA: 1)
gpg: 沒有找到任何絕對信任的密鑰
再次運行curl -L get.rvm.io | bash -s stable
使用
source /usr/local/rvm/scripts/rvm
命令 使rvm當即生效
rvm list known查看當前可用ruby版本
從可用的ruby版本中選一個進行安裝,可是所要安裝的版本必需要大於2.2.2
rvm install x.x.x
rvm use x.x.x當即使用新安裝的ruby版本
運行gem install redis

報錯:

Creating cluster
[ERR] Sorry, can't connect to node 172.0.0.180:6379

有可能的緣由就是
一、172.0.0.180 的 redis服務沒開啓:
查看一下
[root@redis_2 ~]# ps aux | grep redis
root 2859 0.0 0.7 141012 7652 ? Ssl 18:19 0:00 /servers/redis/bin/redis-server ::1:6379 [cluster]

若是還沒啓動,執行:
[root@redis_2 ~]# /servers/redis/bin/redis-server /servers/redis/redis.conf

二、172.0.0.180 對應的端口 是否對外開放:
能夠在172.0.0.188 鏈接看一下:
[root@redis_1 src]# /servers/redis/bin/redis-cli -h 172.0.0.180
若是出現:
Could not connect to Redis at 172.0.0.180:6379: Connection refused
能夠執行:
[root@redis_2 ~]# firewall-cmd --zone=public --add-port=6379/tcp --permanent
[root@redis_2 ~]# firewall-cmd --reload

發現上面問題都處理了,仍是出現鏈接節點不上的狀況的話,也有多是綁定的ip地址問題:
編輯配置文件:
[root@redis_2 ~]# vim /servers/redis/redis.conf
把bind 指令改成對應的ip
bind 172.0.0.180
關閉在啓動:
[root@redis_2 ~]# /servers/redis/bin/redis-cli shutdown
[root@redis_2 ~]# /servers/redis/bin/redis-server /servers/redis/redis.conf

[ERR] Node 172.168.63.202:7001 is not empty. Either the nodealready knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
解決方法:
1)、將須要新增的節點下aof、rdb等本地備份文件刪除;
2)、同時將新Node的集羣配置文件刪除,即:刪除你redis.conf裏面cluster-config-file所在的文件;
3)、再次添加新節點若是仍是報錯,則登陸新Node,./redis-cli–h x –p對數據庫進行清除:
172.168.63.201:7001> flushdb #清空當前數據庫

一直都在Waiting for the cluster to join.......
配置集羣的時候在redis.conf配置文件中bind ip的時候是這樣進行綁定的:
bind 127.0.0.1 機器ip
將綁定方式修改成:
bind 機器ip

錯誤信息:(error) MOVED 11469 172.16.35.15:7002

應該是你沒有啓動集羣模式(即缺乏了那個"-c"):
redis-cli -c -h yourhost -p yourpost

https://www.evernote.com/shard/s736/sh/cc73e00c-1c0e-4bc2-9997-49e89f096c2f/0e356a4db3b927ed

相關文章
相關標籤/搜索