Redis進階實踐之十一 Redis的Cluster叢集搭建
一、引言
本文件只對Redis的Cluster叢集做簡單的介紹,並沒有對分散式系統的所涉及到的概念做深入的探討。本文只是針對如何設定叢集、測試和操作叢集做了簡述,並且從使用者的角度描述了系統的行為,並不涉及Redis叢集規範中所包含的細節。但是,本教程試圖從終端使用者的角度來解釋有關Redis的Cluster叢集的可用性和一致性的特點,並以簡單易懂的方式講解。
請注意,本教程需要使用Redis 3.0版本或更高版本。
如果您打算部署Redis的Cluster叢集,即使不是嚴格的要求,我們也建議閱讀更正式的規範。不過,從這篇文件開始,我們可以先使用Redis Cluster叢集,然後再閱讀規範也是一個不錯的主意。
二、Redis的Cluster模式介紹
1、Redis群集101
Redis叢集提供了一種執行Redis裝置的方式,並且資料可以在多個Redis節點間自動分配的。Redis叢集在分割槽期間也能提供一定程度的可用性,實際上,就是說當某些節點發生故障或無法通訊時,叢集能夠繼續執行。 但是,如果發生較大故障(例如,大多數主站伺服器不可用時),群集會停止執行。
那麼從實際角度而言,您使用Redis Cluster能獲得什麼呢?
1、在多個節點之間自動分割資料集的能力。
2、在節點子集遇到故障或無法與叢集其餘部分通訊時繼續執行的能力。
2、Redis群集TCP埠
每個Redis群集的節點都需要開啟兩個TCP連線,由於這兩個連線就需要兩個埠,分別是用於為客戶端提供服務的常規Redis TCP命令埠
第二個大號埠用於群集匯流排,即使用二進位制協議的節點到節點通訊通道。 節點使用群集匯流排進行故障檢測,配置更新,故障轉移授權等。 客戶端不應嘗試與群集匯流排埠通訊,為了保證Redis命令埠的正常使用,請確保在防火牆中開啟這兩個埠,否則Redis群集節點將無法通訊。
命令埠和叢集匯流排埠偏移量是固定的,始終為10000。
請注意,為了讓Redis群集正常工作,您需要為每個節點:
1、用於與客戶端進行通訊的普通客戶端通訊埠(通常為6379)對所有需要到達群集的客戶端以及所有其他群集節點(使用客戶端埠進行金鑰遷移)都是開放的。
2、叢集匯流排埠(客戶端埠+ 10000)必須可從所有其他叢集節點訪問。
如果您不開啟這兩個TCP埠,則您的群集將無法正常工作。
叢集匯流排使用不同的二進位制協議進行節點到節點的資料交換,這更適合於使用很少的頻寬和處理時間在節點之間交換資訊。
3、Redis叢集和Docker
目前,Redis群集不支援NAT地址環境,並且在IP地址或TCP埠被重新對映的一般環境中。
Docker使用一種叫做埠對映的技術:Docker容器中執行的程式可能會暴露在與程式認為使用的埠不同的埠上。 這對於在同一伺服器中同時使用相同埠執行多個容器很有用。
為了使Docker與Redis Cluster相容,您需要使用Docker的主機聯網模式。 請檢視Docker文件中的--net = host選項以獲取更多資訊。
4、Redis叢集資料分片
Redis叢集沒有使用一致的雜湊,而是一種不同的分片形式,其中每個 key 在概念上都是我們稱之為雜湊槽的部分。
Redis叢集中有16384個雜湊槽,為了計算給定 key 的雜湊槽,我們簡單地取16384模的CRC16。
Redis叢集中的每個節點負責雜湊槽的一個子集,例如,您可能有一個具有3個節點的叢集,其中:
1、節點A包含從0到5500的雜湊槽。
2、節點B包含從5501到11000的雜湊槽。
3、節點C包含從11001到16383的雜湊槽。
這允許輕鬆地新增和刪除叢集中的節點。例如,如果我想新增一個新節點D,我需要將節點A,B,C中的一些雜湊槽移動到D。同樣,如果我想從叢集中刪除節點A,我可以只移動由A使用的雜湊槽到B和C,當節點A將為空時,我可以將它從群集中徹底刪除。
因為將雜湊槽從一個節點移動到另一個節點不需要停機操作,新增和移除節點或更改節點佔用的雜湊槽的百分比也不需要任何停機時間。
只要涉及單個命令執行(或整個事務或Lua指令碼執行)的所有 key 都屬於同一雜湊插槽,Redis群集就支援多個 key 操作。使用者可以使用稱為雜湊標籤的概念強制多個 key 成為同一個雜湊槽的一部分。
Hash標記記錄在Redis叢集規範文件中,但要點是如果在關鍵字{}括號內有一個子字串,那麼只有該花括號“{}”內部的內容被雜湊,例如 this{foo}key 和 another{foo}key 保證在同一雜湊槽中,並且可以在具有多個 key 作為引數的命令中一起使用。
5、Redis叢集之主從模型
為了在主伺服器節點的子集失敗或不能與大多數節點通訊時保持可用,Redis叢集使用主從模型,其中每個雜湊槽從1(主伺服器本身)到N個副本(N -1個附加從節點)。
在我們具有節點A,B,C的示例的群集中,如果節點B失敗,則群集無法繼續,因為我們沒有辦法再在5501-11000範圍內提供雜湊槽。然而,當建立叢集時(或稍後),我們為每個主伺服器節點新增一個從伺服器節點,以便最終叢集由作為主伺服器節點的A,B,C以及作為從伺服器節點的A1,B1,C1組成,如果節點B發生故障,系統能夠繼續執行。節點B1複製B,並且B失敗,則叢集將促使節點B1作為新的主伺服器節點並且將繼續正確地操作。
但請注意,如果節點B和B1在同一時間發生故障,則Redis群集無法繼續執行。
6、Redis叢集一致性保證
Redis 叢集無法保證很強的一致性。實際上,這意味著在某些情況下,Redis 叢集可能會丟失系統向客戶確認的寫入。
Redis叢集可能會丟失寫入的第一個原因是因為它使用非同步複製。這意味著在寫入期間會發生以下事情:
1、你的客戶端寫給主伺服器節點 B
2、主伺服器節點B向您的客戶端回覆確認。
3、主伺服器節點B將寫入傳播到它的從伺服器B1,B2和B3。
正如你可以看到主伺服器節點 B 在回覆客戶端之前不等待B1,B2,B3的確認,因為這會對Redis造成嚴重的延遲損失,所以如果你的客戶端寫入了某些東西,主伺服器節點 B 確認寫入,就在將寫入傳送給它的從伺服器節點儲存之前系統崩潰了,其中一個從站(沒有收到寫入)可以提升為主站,永遠丟失寫入。
這與大多數配置為每秒將資料重新整理到磁碟的資料庫所發生的情況非常相似,因為過去的經驗與傳統資料庫系統有關,不會涉及分散式系統,因此您已經能夠推斷這種情況。同樣,通過強制資料庫在回覆客戶端之前重新整理磁碟上的資料,這樣可以提高一致性,但這通常會導致效能極低。這與Redis Cluster中的同步複製相當。
基本上,效能和一致性之間需要權衡。
Redis叢集在絕對需要時也支援同步寫入,通過WAIT命令實現,這使得丟失寫入的可能性大大降低,但請注意,即使使用同步複製,Redis叢集也不可能實現完全的一致性:總是有可能會發生故常,在無法接受寫入的從裝置被選為主裝置的時候 。
還有另一個值得注意的情況,Redis叢集也將丟失資料的寫入,這種情況發生在網路分割槽的時候,客戶端與包含至少一個主伺服器的少數例項隔離。
以A,B,C,A1,B1,C1三個主站和三個從站組成的6個節點叢集為例。還有一個客戶,我們會呼叫Z1。
分割槽發生後,可能在分割槽的一側有A,C,A1,B1,C1,另一側有B和Z1。
Z1仍然能夠寫入B,它也會接受Z1的寫入。如果分割槽在很短的時間內恢復,則群集將正常繼續。但是,如果分割槽使用比較長的時間將B1提升為多數側分割槽的主裝置,則Z1傳送給B的寫入操作將丟失。
請注意,Z1能夠傳送給B的寫入量有一個最大視窗(maximum window):如果分割槽多數側有足夠的時間選擇一個從裝置作為主裝置,那麼少數側的每個主節點將停止接受寫操作。
這個時間值是Redis叢集非常重要的配置指令,稱為 node timeout (節點超時)。
在節點超時過後,主節點被認為是失效的,並且可以被其副本之一替換。類似地,節點超時過後,主節點無法感知大多數其他主節點,它進入錯誤狀態並停止接受寫入。
7、Redis群集配置引數
我們即將建立示例叢集部署。在繼續之前,讓我們介紹一下Redis Cluster在redis.conf檔案中引入的配置引數。有些命令的意思是顯而易見的,有些命令在你閱讀下面的解釋後才會更加清晰。
1、cluster-enabled <yes/no>:如果想在特定的Redis例項中啟用Redis群集支援就設定為yes。 否則,例項通常作為獨立例項啟動。
2、cluster-config-file <filename>:請注意,儘管有此選項的名稱,但這不是使用者可編輯的配置檔案,而是Redis群集節點每次發生更改時自動保留群集配置(基本上為狀態)的檔案,以便能夠 在啟動時重新讀取它。 該檔案列出了群集中其他節點,它們的狀態,持久變數等等。 由於某些訊息的接收,通常會將此檔案重寫並重新整理到磁碟上。
3、cluster-node-timeout <milliseconds>:Redis群集節點可以不可用的最長時間,而不會將其視為失敗。 如果主節點超過指定的時間不可達,它將由其從屬裝置進行故障切換。 此引數控制Redis群集中的其他重要事項。 值得注意的是,每個無法在指定時間內到達大多數主節點的節點將停止接受查詢。
4、cluster-slave-validity-factor <factor>:如果設定為0,無論主裝置和從裝置之間的鏈路保持斷開連線的時間長短,從裝置都將嘗試故障切換主裝置。 如果該值為正值,則計算最大斷開時間作為節點超時值乘以此選項提供的係數,如果該節點是從節點,則在主鏈路斷開連線的時間超過指定的超時值時,它不會嘗試啟動故障切換。 例如,如果節點超時設定為5秒,並且有效因子設定為10,則與主裝置斷開連線超過50秒的從裝置將不會嘗試對其主裝置進行故障切換。 請注意,如果沒有從伺服器節點能夠對其進行故障轉移,則任何非零值都可能導致Redis群集在主伺服器出現故障後不可用。 在這種情況下,只有原始主節點重新加入叢集時,叢集才會返回可用。
5、cluster-migration-barrier <count>:主裝置將保持連線的最小從裝置數量,以便另一個從裝置遷移到不受任何從裝置覆蓋的主裝置。有關更多資訊,請參閱本教程中有關副本遷移的相應部分。
6、cluster-require-full-coverage <yes / no>:如果將其設定為yes,則預設情況下,如果key的空間的某個百分比未被任何節點覆蓋,則叢集停止接受寫入。 如果該選項設定為no,則即使只處理關於keys子集的請求,群集仍將提供查詢。
三、建立和使用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
正如您所看到的那樣,啟用群集模就是使用 cluster-enabled 這個指令。 每個Redis的例項還包含儲存此節點配置資訊的檔案的路徑,預設情況下為nodes.conf。 這個檔案內容永遠不要人為地去修改,但是可以修改其名稱,它僅在Redis叢集例項啟動時生成,並在每次需要時進行更新。
請注意,按預期工作的最小群集需要至少包含三個主節點。 對於第一次測試,強烈建議啟動一個由三個主伺服器節點和三個從伺服器節點組成的六個節點群集。我們通過以下步驟來一步一步的搭建Redis的Cluster叢集環境。
1、我們建立相關目錄,主資料夾是redis-cluster,在此資料夾下建立6個子資料夾,名稱分別是:7000,7001,7002,7003,7004,7005,該目錄以我們將在任何給定目錄內執行的例項的埠號命名。
然後建立6個子目錄,如下圖:
mkdir redis-cluster cd redis-cluster mkdir 7000 7001 7002 7003 7004 7005
2、目錄建立好後,我們把Redis原始檔裡面包含的配置檔案redis.conf拷貝一份,存放在7000目錄下,然後對其配置項進行修改,這個配置檔案Redis.conf會作為其他Redis例項的配置檔案的模板,並拷貝到其他目錄。
由於我們是做測試,並沒有啟動6個真正的物理節點,而是把6個Redis例項都部署在了同一臺Linux伺服器上,地址:192.168.127.130,為了區分Redis例項,我們是以不同的埠號來區分Redis例項的。然後我們修改Redis.conf的配置檔案,修改項如下:
bind 192.168.127.130 //繫結伺服器IP地址 port 7000 //繫結埠號,必須修改,以此來區分Redis例項 daemonize yes //後臺執行 pidfile /var/run/redis-7000.pid //修改pid程序檔名,以埠號命名 logfile /root/application/program/redis-cluster/7000/redis.log //修改日誌檔名稱,以埠號為目錄來區分 dir /root/application/program/redis-cluster/7000/ //修改資料檔案存放地址,以埠號為目錄名來區分 cluster-enabled yes //啟用叢集 cluster-config-file nodes-7000.conf //配置每個節點的配置檔案,同樣以埠號為名稱 cluster-node-timeout 15000 //配置叢集節點的超時時間,可改可不改 appendonly yes //啟動AOF增量持久化策略 appendfsync always //發生改變就記錄日誌
3、7000目錄下的Redis.conf配置檔案修改後,分別拷貝到其他子目錄,依次為:7001,7002,7003,7004,7005,根據上面的配置,我們只需修改和埠號有關的專案,在Linux系統下,我們通過命令:%s/7000/7001/g,:%s/7000/7002/g,:%s/7000/7002/g,:%s/7000/7003/g,:%s/7000/7004/g,:%s/7000/7005/g 分別進行全域性替換,並儲存,完成對其他子目錄下的配置檔案的修改。
4、我們安裝Redis的Cluster叢集,需要使用Ruby命令,所以我們必須安裝對Ruby的支援。
在此說明一下,以前的Redis版本下,需要安裝Ruby和Rubygems,但是最新的版本不需要了,只要安裝Ruby,Rubygems就會自動安裝。
yum install ruby //安裝ruby yum install rubygems //安裝rubygems,最新版本會自動安裝
5、我們安裝完 Ruby 和 Rubygems 後,還需要繼續安裝Redis的Ruby介面程式。
gem install redis
安裝Redis的ruby介面程式,可能會提示如下,錯誤:redis requires ruby version 2.2.2,怎麼辦呢?如果是第一次遇到這個問題,可能會困擾你一陣子,我這裡也有解決方案,幫你解憂。地址如下:http://www.cnblogs.com/PatrickLiu/p/8454579.html,按步驟執行就可以,一切順利。
6、開始啟動我們6個Redis例項,並且要指定配置檔案,這些配置檔案分別在各自的子目錄下面。
cd 7000 redis-server redis.conf cd 7001 redis-server redis.conf cd 7002 redis-server redis.conf cd 7003 redis-server redis.conf cd 7004 redis-server redis.conf cd 7005 redis-server redis.conf
7、建立叢集,執行redis-trib.rb指令碼,這個指令碼檔案可以拷貝出來,我是把它放在這個目錄:/root/application/program/redis/,當然在這個目錄下,也有其他檔案,比如redis-cli,redis-server等。
ruby redis-trib.rb create --replicas 1 192.168.127.130:7000 192.168.127.130:7001 192.168.127.130:7002 192.168.127.130:7003 192.168.127.130:7004 192.168.127.130:7005
我們有Redis叢集命令列實用程式redis-trib的幫助,Ruby實用程式對例項執行特殊命令以建立新叢集,檢查或重新設定現有叢集,等等。 redis-trib實用程式位於Redis原始碼分發的src目錄中,當然也可以拷貝到其他目錄中,以方便使用。 您需要安裝redis gem才能執行redis-trib。
這裡使用的命令是create,因為我們想建立一個新的叢集。 選項--replicas 1 意味著我們需要為每個建立的主伺服器節點建立一個從伺服器節點。其他引數是我想用來建立新叢集的例項的地址列表。
顯然,我們要求的唯一設定是建立一個具有3個主站和3個從站的叢集。
8、 如果一切順利,你會看到類似這樣的訊息: [OK] All 16384 slots covered, 這意味著至少有一個主例項服務於每個16384可用的插槽,成功建立了Redis的Cluster叢集環境。
9、分別登陸7000,7001,7002Redis的例項客戶端,進行測試。效果如圖:
1、登陸7000操作:
redis-cli -c -h 192.168.127.130 -p 7000
2、登陸7001操作:
redis-cli -c -h 192.168.127.130 -p 7001
3、登陸7002操作:
redis-cli -c -h 192.168.127.130 -p 7002
10、通過Cluster Nodes命令和Cluster Info命令來看看叢集效果。
11、在叢集上通過增加資料來測試叢集效果。直接看截圖效果吧:
每個Redis的節點都有一個ID值,此ID將被此特定redis例項永久使用,以便例項在叢集上下文中具有唯一的名稱。 每個節點都會記住使用此ID的每個其他節點,而不是通過IP或埠。IP地址和埠可能會發生變化,但唯一的節點識別符號在節點的整個生命週期內都不會改變。 我們簡單地稱這個識別符號為節點ID。
四、使用建立群集指令碼建立Redis群集
如果您不想通過如上所述手動配置和執行單個例項來建立Redis群集,則有一個更簡單的系統可以代替以上操作(但您不會學到相同數量的操作細節)。
只需在Redis發行版中檢查 utils/create-cluster 目錄即可。 裡面有一個名為create-cluster的指令碼(與其包含的目錄名稱相同),它是一個簡單的bash指令碼。 要啟動具有3個主站和3個從站的6個節點群集,只需輸入以下命令:
1、create-cluster start
2、create-cluster create
當redis-trib實用程式希望您接受叢集佈局時,在步驟2中回覆yes。
您現在可以與群集互動,預設情況下,第一個節點將從埠30001開始。 完成後,停止群集:
1、create-cluster stop.
請閱讀此目錄中的自述檔案以獲取有關如何執行指令碼的更多資訊。
五、測試故障轉移
注意:在此測試期間,應該執行一致性測試應用程式時開啟選項卡。
為了觸發故障轉移,我們可以做的最簡單的事情(這也是分散式系統中可能發生的語義上最簡單的故障)是使單個程序崩潰,在我們的當前的情況下就是單個主程序。
我們可以識別一個叢集並使用以下命令將其崩潰:
$ 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都是主伺服器節點。 讓我們用 DEBUG SEGFAULT 命令使節點7002崩潰:
$ redis-cli -p 7002 debug segfault Error: Server closed the connection
現在我們可以看一致性測試的輸出以檢視它報告的內容。
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) |
正如您在故障轉移期間所看到的,系統無法接受578次讀取和577次寫入,但是在資料庫中未建立任何不一致。 這聽起來可能會出乎意料,因為在本教程的第一部分中,我們宣告Redis群集在故障轉移期間可能會丟失寫入,因為它使用非同步複製。 我們沒有說的是,這種情況不太可能發生,因為Redis會將答覆傳送給客戶端,並將命令複製到從伺服器,同時,因此會有一個非常小的視窗來丟失資料。 但是很難觸發這一事實並不意味著這是不可能的,所以這不會改變Redis叢集提供的一致性保證。
現在我們可以檢查故障轉移後的群集設定(注意,在此期間,我重新啟動了崩潰的例項,以便它重新加入作為從屬群集):
$ 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
現在,主伺服器節點正在埠7000,7001和7002上執行。以前是主伺服器節點,即執行在埠7005上的Redis例項,現在是7002的從伺服器節點。
Node ID ip:port flags: master, slave, myself, fail, ... if it is a slave, the Node ID of the master Time of the last pending PING still waiting for a reply. Time of the last PONG received. Configuration epoch for this node (see the Cluster specification). Status of the link to this node. Slots served...
六、手動故障轉移
有時,強制進行故障轉移並不會在主伺服器上導致任何問題。例如,為了升級其中一個主節點的Redis程序,最好將其故障轉移,以便將其轉變為一個對可用性影響最小的從伺服器。
Redis Cluster使用CLUSTER FAILOVER命令支援手動故障轉移,該命令必須在要故障轉移的主伺服器的一個從伺服器上執行。
手動故障轉移是比較特殊的,並且與實際主控故障導致的故障轉移相比更安全,因為它們是以避免資料丟失的方式發生,只有在系統確定新主伺服器節點處理完全部來自舊主伺服器節點的複製流後才將客戶從原始主伺服器節點切換到新主伺服器節點。
這是您在執行手動故障轉移時在從伺服器節點的日誌中看到的內容:
#接受使用者的手動故障轉移請求。 #已暫停的主伺服器手動故障轉移接收復制的偏移量:347540 #處理所有主伺服器節點的複製流,手動故障轉移可以開始。 #選舉開始延遲0毫秒(等級#0,偏移量347540)。 #為epoch 7545啟動故障轉移選舉。 #故障轉移選舉勝出:我是新主人。 # 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.
基本上連線到我們正在故障轉移的主伺服器節點上的客戶端都已停止工作。與此同時,主伺服器節點將其複製偏移量傳送給從伺服器節點,該從伺服器節點將等待達到其側面的偏移量。當達到複製偏移量時,將啟動故障轉移,並向舊主伺服器通知配置開關。 當舊主伺服器節點上的客戶端被解鎖時,它們會被重定向到新主伺服器。
七、總結
今天就寫到這裡了,關於Cluster的內容還沒有寫完,有關動態擴容的內容將在下一篇文章做詳細介紹。這篇文章對很多東西沒有做更細緻的探討,只是從使用者的角度來簡單說明一下如何搭建Redis的Cluster叢集環境。革命尚未成功,我還需努力。我把原文的地址也貼出來,裡面的內容不完全一樣,我按著我的理解寫的更詳細了。地址如下:【Redis cluster tutorial】