水平有限,如果您在閱讀過程中發現有翻譯的不合理的地方,請留言,我會盡快修改,謝謝。
這是一篇對Redis集群的入門介紹,這里不會使用復雜難懂的分步式系統概念。這里提供的指導有集群 的安裝、測試,和操作,不函蓋Redis集群規范中的細節,而只是站在用戶的角度來描述系統的行為方式。
這個教程試圖從最終用戶角度,以簡單易懂的方式來講解Redis集群高可用性和一至性的特點。
注意,這個教程需要redis的版本為3.0及以上版本。
如果你打算運行更嚴格的Redis集群部屬,即使不是特別的需要 ,也非常建議閱讀正式的規范。當然從這篇文檔開始也是不錯的主意,花一些時間在Redis集群上,然后再閱讀規范。
Redis Cluster 101
Redis集群提供 數據自動分片到多個Redis節點的方式運行Redis實例,Redis 集群在分區期間提供了一定程度的可用性,實際上是當節點掛掉或不能通信時繼續運行的能力。然而會在重大故障情況下Redis會停止運行(eg.絕大多數的主節點不可用)
在實際情況中,你想從Redis集群中得到什么呢?
》自動分割數據集到多個節點的能力
》子節點故障或者不能和集群內的其它節點通信時,繼續運行的能力
Rdis Cluster TCP 端口
每個Redis集群節點需要打開兩個TCP連接。一個用於服務端和客戶端連接的一般端口,例好6379,另一個端口是通過數據端口加10000得到,這個例子中是16379。第二個較高的端口用於集群總線,這是一個使用二進制協議端到端的通信通道。集群通道用於節點的故障偵查,配置更新,故障轉移授權等。客戶端總是和正常的Redis命令端口通信 ,永遠不要嘗試和集群總線通信.請確保你的防火牆開放這兩個端口,不然集群節點不能相互通信。
命令端口和集群總線端口偏移是固定的,偏移量總是10000.
注意為了Redis集群正常的工作,對每一個節點:
1.用於和客戶端通信的標准通信端口(通常是6379)對所有需要和聯系集群和集群其它節點(使用用於鍵遷移的客戶端端口) 的客戶端開放。
2.集群總線端口(客戶端端口+10000)必須可以被集群的其它節點訪問到。
Redis集群和Docker
現在Redis集群不支持 NATed環境和ip地址或TCP端口被重新映射的環境。
Docker使用一種稱為端口映射的技術:程序在Docker容器中運行的端口,可能與Docker暴露出的端口不一至。這對於同時在一個服務器內多個容器使用同一個端口是非常有用的。
為了使Docker和Redis集群兼容,你需要使用Docker的主機網絡模式。請在Docker document查看更多的--net=host相關信息。
Redis集群數據分片
Redis集群不使用一致性的hash算法,而使用數據分片(sharding),每一個鍵概念上都是哈希槽(hash slot)的一部分。在Redis集群里有16384個哈希槽,我們使用鍵進行CRC16算法運算再和16384進行模運算來計算給定的key屬於哪個槽。
Redis集群里的每個節點都是哈希槽(hash slot)的子集。例,假如你的集群有3個節點:
》節點A包含0到5500哈希槽(hash slot)。
》節點B包含5501到11000哈希槽(hash slot)。
》節點C包含11001到16384哈希槽(hash slot)。
這樣允許你輕松的增減集群節點。例,假如你想添加 一個新的節點D,需要從節點A,B,C移動哈希槽(hash slot)到D節點。同樣的,如果想去掉節點A,只需要將A的哈希槽(hash slot)移動到B和C,當A的哈希槽(hash slot)為空的時候就可以從集群中刪除它了。
因為從一個節點移動哈希槽(hash slot)到其它節點不需要中斷Redis運行,添加和移除節點,或者修改節點哈希槽(hash slot)所占用的百分比,不需要任何停機時間。
Redis集群支持運行多鍵操作,在一個命令執行中所有涉及到的鍵(整個事務或Lua執行角本)都屬於同一個哈希槽。用戶可以通過使用哈希標簽(hash tags)概念來強制多個鍵歸屬於相同哈希槽的一部分。
哈希標簽(hash tags)在Redis集群規范中有記載,但是關鍵在於鍵的{}內有子字符,只有{}中的字符串才會被哈希,例 this{foo}key和 another{foo}key被保證在同一個哈希槽中(hash slot),並且可以在具有多鍵作為參數的命令中一起使用。
Redis集群主從模式
為了當子節點故障或者不能和其它大部分節點通信時能正常運行,Redis集群修改主從模式(master-slave),每一個哈希槽(hash slot)有1(master自己)到N個復制(N-1個附加的從節點).
在我們的例子中集群有3個節點A,B,C.如果B節點故障則集群不能繼續運行,因為我們沒有辦法再為5501-11000范圍內的哈希槽(hash slot)提供服務。
然而當集群被創建時(或在后面的時間)我們每一個主節點添加一個從節點,這樣最終的主節點由A,B,C組成,從節點為A1,B1,C1,這樣的系統當節點B故障時依然可以運行。
節點B1復制B節點,當B故障時,集群將把B1節點晉升為新的主節點,這樣系統會繼續運行。
Redis一致性的保證
Redis集群不保證數據的強一致性。實際上這意味着在某些情況下Redis集群可能會丟失已經被系統確認的客戶端寫操作。
使用異步復制是集群丟失寫命令的一個原因,這意味在寫操作期間,會發生下列情況:
》你的客戶端對主節點B 寫入操作.
》主節點B向客戶端返回OK。
》主節點B將寫入操作傳播到從節點B1,B2,B3。
你可以看到在答復客戶端之前,B節點不會等待B1,B2,B3的確認,由此對於redis來說這將是昂貴的延時處罰,如果你的客戶端發送一此寫入請求,B節點確認寫入請求,但是在發送到從節點寫入之前掛掉了,其中一個從節點(沒有收到寫入命令的節點)提升為主節點,將永久丟失寫入命令。
這和大多數設置為每秒鍾向硬盤刷入數據的數據庫非常像,這樣的場景根據你對傳統數據庫不涉及分布式系統的經驗應該已經能明白問題出現的原因了。就像可以通過在應答客戶端之前強制向硬盤刷入數據來改善一致性一樣,這樣通常會導致性能極大下降。這和Redis集群同步復制的情況是一樣的。
從本質上來說我們要在性能和一致性方面做出一個權衡。
Redis集群在絕對需要時支持同步寫入,可以通過WAIT命令來實現,這樣丟失寫入操作的可能性大大的減少,注意即使使用同步復制,Redis集群也是不支持強一致性的:在更復雜的故障場景下,被提升為主節點的從節點依然有可能沒有收到寫入命令。
還有一種Redis集群會丟失寫入命令的場景需要注意,發生成網絡分區一個客戶端和少數的至少一個主節點的實例被隔離期間。
我們以6個節點A,B,C,A1,B1,C1 組成的3個主節點和3個從節點集群為例。還有一個客戶端我們稱為Z1.
當網絡分區發生后,可能會出現一邊的分區有A,C,A1,B1,C1,別一邊有B和Z1.
Z1仍然可以先向B發送寫入命令,B會接受寫入請求。如果分區在很短的時間內恢復,集群會正常運行。然而,如果分區持續的時間足夠長以至於在節點數量較多的分區內B1被提升為主節點,那么Z1發送的寫入請求將會丟失。
Z1可以向B節點發送的請求寫入量有一個最大的上限:如果占用的時間足夠長,有大量節點的分區將會選擇一個從節點晉升為主節點,在有大量節點的分區內的所有主節點將停止接受寫入命令。
這一段時間是非常重要的Redis集群配置,稱為節點超時時間(node timeout)
節點超節點時時間(node timeout)過后,一個主節點將被認為已經下線,並且可以被他的從節點替換。
同樣的超時節點時間過后,沒有一個主節點能夠感知到大多數的其它主節點,它將進入一個錯誤狀態,並停止接收寫入命令。
Redis 集群配置參數
我們將創建一個集群部屬實例。在開始之前,先介紹Redis集群的redis.conf文件內引入的配置參數,有一些很易於理解,其它的只要你持續閱讀就能很明白。
>
cluster-enabled
<yes/no>:如果設置為yes,那么將在一個特殊的Redis實例內啟用支持Redis集群.否則實例做為普通的獨立實例啟動。
>
cluster-config-file
<filename>:注意,不用理這個選項的名字,這不是用戶可編輯的配置文件,而是Redis集群節有變動的時自動維護的集群配置(狀態,和基本信息)的文件,為了能在啟動時重新讀取它。這個文件列出了集群中的節點,和節點的狀態,持久變量等等。經常這個文件做為消息接收的結果被重寫並刷入到硬盤
>
cluster-node-timeout
<milliseconds>: Redis集群節點最長的不可用時間,如果沒有設置他將做為故障處理。如果主節在指定的時間內無法訪問,將通過他的從節點故障轉移。在Redis集群內這個參數還控制其它重要的東西.尤其是,在指定的時間內每一個不能訪問絕大多數的主節點的節點,將會停止接受請求。
>
cluster-slave-validity-factor
<factor>: 如果設置為0,從節點將總是嘗試故障切換主節點,不理會主節點和從節點之間的鏈路保持斷開的時間。如果設置為正數,最大的斷開時間被計算為節點超時(node timeout)的值乘以提供此選項的因子(factor),並且如果這個節點是個從節點,主節點鏈路斷開的時間大於指定的時間也不嘗試啟動故障切換。例如,如果節點超時時間設置為5秒,這個有效因子設置為10,從節點和主節點的斷開時間超過50秒也不會嘗試故障切換它的主節點。注意,當主節點故障時,如果沒有從節點故障切換主節點,任何不同於0的值會導致Redis集群不可用。在這種情況下,只有原來的主節點重新加入到集群,集群才會返回可用。
>
cluster-migration-barrier
<count>:主節點保持連接的最小從節點的數量,用於別的從節點遷移到不再被其它從節點覆蓋的主節點。相關更詳細的信息,請閱讀本教程中關於復制副本遷移的相應部分。
>
cluster-require-full-coverage
<yes/no>:如果設置為yes,也是它的默認值,如果某些鍵空間的百分比沒有被任何一個節點所覆蓋集群將停止接收寫入命令。如果設置為no,即使只能處理有關鍵的子集求,集群依然提供查詢。
創建和使用Redis集群
注意:要手動的創建Redis集群學習,學習某些業務方面很重要。然而如果你想使集群啟動並快速運行,跳過這一章節和下一節直接去創建Redis集群使用創建集群角本(Creating a Redis Cluster using the create-cluster script).
要創建集群,我們需要做的第一件事是有幾個空的Redis實例在集群模式中運行。這基本上意味着不是使用一般的Redis實例創建集群,因為需要設置為特殊的模式,所以Redis實例將啟用集群特定的功能和命令。
下面是一個最小的Redis集群配置文件:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
如同你所看到的簡單的cluster-enabled指令開啟了集群模式。每一個實例均包含用於保存節點的配置文件路徑,默認是nodes.conf。這個文件不是人為創建的;它是通過Redis集群實例啟動時創建的,並在需要的時候對它進行修改。
注意,這個最小的集群(minimal cluster)按預期的工作需要包含至少3個主節點。做為你的第一次嘗試,強烈建議你啟用3個主節點和3個從節點,6個節點集群。
這樣做,進入一個新的目錄,並創建下面的目錄並以實例的端口號命名,我們將在這些給定的目錄內運行實例。
像這樣:
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
在每個目錄內創建一個redis.conf文件,從7000到7005。使用上邊的小例子做為你的配置文件的模板,但是請確保依據目錄名使用正確的端口號來替換端口號7000.
現在復制你的redis-server 可執行文件,從Github上不穩定分支內的最新源碼編譯的,復制到cluster-test目錄,最后使用你最喜歡的終端程序打開6個終端標簽。
在每個終端標簽內像這樣啟動實例:
cd 7000
../redis-server ./redis.conf
正如你在每個實例的log中看到的,由於沒有nodes.conf存在,每一個節點賦給自己一個新的ID.
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
這個ID將被把定的實例永久使用,以便這個實例在集群的上下文中有唯一的名稱。每一個節點使用這個ID來記錄其它節點,而不是使用IP或端口號.IP地址或端口號可能會變動,而在節點的整個生命周其唯一的節點標識符是不會變動的。我們簡單的稱呼這個標識符為
Node ID。
創建集群
現在我們有很多實例在運行,我們需要通過向節點寫入一些有意義的配置來創建我們的集群。
這是很容易完成的,因為我們可以使用Redis集群命令行工具redis-trib的幫助,Ruby程序在實例上執行特殊的命令來創建新集群,檢查或reshard現有的集群,等等。
redis-trib實用工具在Redis源碼發行版中src目錄內。你需要安裝redis gem才能運行redis-trib。
gem install redis
要創建集群只需輸入:
./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
這里使用的是create命令,因為我們想創建一個新的集群。選項--replicas 1 意味着我們想為每一個創建的主節點一個從節點。其它的參數是我們用於創建新集群的實例地址列表。
很顯然這唯一的設置和我們的需求是創建一個有3個主節點和3個從節點的集群。
Redis-trib 會給你一個建議配置,通過輸入yes來接受建議配置,集群將被配置和加入,這意味着,實例將被引導互相交流。最后,如果一切正常,你會看到下面的信息:
[OK] All 16384 slots covered
這意味着至少有一個主節點的實例為每一個16384個可用槽提供服務。
