1. 程式人生 > >Redis叢集教程(Redis cluster tutorial)

Redis叢集教程(Redis cluster tutorial)

       本文件以溫和的方式介紹Redis叢集,不使用複雜的方式來理解分散式系統的概念. 它介紹瞭如何建立、測試和使用一個叢集,沒有詳細的覆蓋Redis叢集說明書 ,只是從使用者的角度描述了系統的特性.
不管怎麼樣,本教程嘗試從終端使用者的角度來提供關於Redis 叢集 可用性和一致性的資訊。
注意:本教程需要Redis 3.0版本或者更高.
如果你計劃執行一個重要的Redis 叢集部署,更多的正規說明書建議閱讀下,雖然不嚴格要求.    不管怎樣,這是一個好的建議從本文件開始玩轉Redis 叢集一段時間,稍後閱讀Redis 叢集詳細說明書.

一、Redis叢集101

Redis叢集提供一個將資料通過多個Redis節點自動分片的方式來安裝Redis.
Redis叢集也通過分割槽提供可用性程度,那就是在實踐中當一些節點down掉或失去聯絡,仍然可以繼續工作的能力. 不管怎樣,大量節點出現故障時(例如當大多數的主節點不可用)Redis叢集會停止工作. 所以在實踐中,你從Redis叢集中得到了什麼?
  • 在多個節點中資料自動分片的能力.
  • 當一部分節點失敗或者不能與叢集其餘節點通訊 ,叢集仍然可以工作的能力.

二、Redis叢集TCP埠

每個Redis叢集節點需要開通兩個TCP 連線。正常的Redis TCP埠被用作服務端的埠,例如6379,服務埠加上10000得到的作為資料埠,例如16379。 第二個高階口被用於叢集匯流排,即使用二進位制協議的點對點的通訊通道。  叢集匯流排被節點用來作 失敗探測、配置更新、故障轉移授權認可等等。 客戶端不應該嘗試聯絡叢集匯流排埠,但是總與正常的命令列埠連線。無論如何,確保這兩個埠在你的防火牆是開啟的,否則Redis叢集節點不能互相聯絡. 命令列埠和叢集匯流排埠的偏移量書固定的,總是10000. 注意為了讓叢集正確的工作,你需要為每個節點:
  1. 命令列埠對需要訪問叢集的客戶端可達, 加上所有其餘的叢集節點(使用命令列埠做keys轉移)。
  2. 叢集匯流排埠(命令列埠+10000)對叢集中的所有節點可達.
如果你不開啟兩個TCP埠,你的叢集將不會預期的工作. 叢集匯流排使用一個不同的、二進位制協議來進行點對點的資料交換, 這種方式使用少量的頻寬和處理時間使其更適合節點之前的資料交換.

三、Redis叢集和Docker

當前的Redis叢集不支援NATted (網路地址轉換)環境和常規下IP地址或埠重新規劃的環境。 Docker使用一個叫埠對映的技術:程式執行在Docker容器中可能暴露一個不同的埠給一認為比較信任的程式使用.  對於為了在同一埠、同一時間、同一服務執行多個服務是非常有用的。 為了使Docker和Redis叢集更好的相容,你需要使用Docker的主機網路模式(host networking mode
),請在Docker文件是檢視更多關於--net=host選項的資訊.

四、Redis叢集資料分片

Redis叢集不使用一致性Hash演算法,使用另一種分片形式----- 每個key被概念性的區分,我們稱之為Hash slot. Redis叢集有16384個hash slot,然後為了計算一個給定key的hash slot是什麼,我們簡單的採用了這個key的CRC16校驗模16384得到.

每個Redis叢集節點負責hash slots總數的的一個子集,舉個例子,你擁有三個節點的Redis叢集:

  • 節點A包含0~5500個hash slots.
  • 節點B包含5501~11000個hash slots.
  • 節點C包含11001~16383個hash slots.
這使得新增和和刪除節點在Redis叢集中變得容易.例如,如果我想加個節點D,我需要從節點A、B、C移動一些hash slot到D節點上. 同樣的,如果我想從叢集中移除節點A,我只需要把服務於A節點的hash slot移動到B和C.當節點A沒有任何的hash slot時,就可以完全的刪除節點A了.
因為從一個節點移動hash slots到另一個節點不需要停止操作、新增和刪除節點、或者改變節點間持有hash slot的百分比,也不需要任何的服務停止.
Redis叢集支援多key操作,只要所有的key在單一的命令列執行(或整個事務,或Lua指令碼執行)所有的key屬於同一個hash slot。 使用者可以強制多key在同一個hash slot,通過使用一個叫hash tags的東西.  Hash tags被記錄在Redis 叢集的規範說明書裡. 但是主旨是如果有一個子串在方括號和一個key之間,只要方括號裡的字串是雜湊的,例如:this{foo}key和another{foo}key 保證在同一個hash slot裡,可以在同一個命令列窗口裡多key操作作為引數使用。

五、Redis叢集主從模型

當叢集中的一部分主節點失敗或不能與多數的節點通訊,為了保持叢集的可用行,Redis叢集使用主從複製模型,每一個hash slot擁有1(主節點自身)到N(N-1額外的副節點)的複製. 在我們的例子中,節點A、B、C,如果叢集中的節點B失敗後,我們沒有辦法繼續服務5501-11000範圍的hash slot。 不管怎樣,當一個叢集被建立時(或之後的一段時間),我們為每一個主節點新增一個副節點,所以最終的叢集A、B、C三個主節點和A1、B1、C1三個副節點組成,如果節點B出現故障時,叢集仍然可以繼續工作。 節點B1代替了節點B,B失敗了,叢集會提升節點B1作為新的主節點,叢集將會繼續正常工作. 注意:如果叢集中的節點B和B1同時失敗了,叢集將不會繼續工作.

六、Redis叢集一致性保證

Redis叢集不能保證較強的一致性。在實踐中意味著,當前條件下,Redis 叢集會丟失寫的資料---系統被客戶端承認. Redis叢集丟失寫資料的首要原因是它使用了非同步複製. 這就意味著你寫資料的時候會有如下情況發生:
  • 你的客戶端寫資料到主節點B.
  • 主節點B迴應你的客戶端一切OK.
  • 主節點複製寫的資料到副節點B1、B2、B3.
就你所看見的,主節點B在迴應客戶端之前不會等待B1、B2、B3的確認,所以這對Redis叢集將是一個潛在的隱患. 所以你的客戶端寫了一些東西,節點B對寫操作進行確認,但是節點B這時候出現故障down了,副本節點的其中一個(資料沒有寫入的那個節點)將會被提升為主節點,永遠的丟失剛寫入的資料. 這跟大多數資料庫配置每秒重新整理資料到磁碟很相似。所以設想下你已經知道原因,通過使用傳統資料庫的過去經驗,沒有涉及分散式系統。同樣的,你可以強制資料庫重新整理資料到磁碟在迴應客戶端之前來提供資料的一致性,但是通常的結果是降低系統的效能。 這個例子等價於Redis叢集的非同步複製. 基本上,需要把效能和一直性權衡考慮. Redis叢集支援同步寫當絕對需要的時候,通過WAIT命令實現, 這使得寫資料丟失率少一點。不管怎樣,值得注意的是Redis叢集不實現強一致性,即使同步複製操作被使用 :更復雜的失敗場景總是可能的,當一個副節點沒有接收到資料之前已經被選為主節點了. 這裡有一個值得注意的Redis將會丟失資料的場景 ,發生在網路分割槽時,一個客戶端和一些例項連線,連線的例項中至少包含一個主節點. 就拿我們由A,B,C,A1,B1,C1 6個節點組成的叢集作為例子,3主點,3個副節點。還有一個客戶端,我們稱它為Z1. 分割槽出現後,可能的情況是,在分割槽的一面有A,C,A1,B1,C1,在另一面有B和Z1。 Z1仍然可以寫資料到B,B也可以接收到Z1寫的資料。如果分割槽在較短時間內恢復完成,叢集可以正常的工作.然而,如果分割槽持續足夠時間讓大分割槽的B1節點成為主節點,Z1寫到B的資料將會丟失. 有一點注意的是Z1寫資料到B的數量有一個最大值視窗:如果足夠時間的流失,使得擁有較多節點的那一個分割槽中的一個副節點被選舉為主節點,擁有較少節點分割槽裡的主節點將停止接收資料的寫入。 節點超時時間在Redis叢集中是一個非常重要的配置指令. 節點超時時間過去之後,一個主節點被認為失敗,可能會被它副節點其中的一個代替.同樣的,節點超時時間過去之後,沒有一個主節點可以感知到其它主節點的大多數,它將進入一個錯誤的狀態、停止資料的寫入.

七、Redis叢集配置引數

我們打算建立一個叢集部署的樣例。在繼續之前,讓我們先介紹下Redis 叢集配置檔案redis.conf 的一些配置引數. 一些引數是很顯然的,另一些引數在你繼續閱讀之後將變的更清晰。
  • cluster-enabled <yes/no>:如果是設定為yes,Redis叢集將支援一個指定的Redis例項;否則這個例項將作為一個普通單獨的例項。
  • cluster-config-file <filename>注意不要管這個選項的名稱,這不是一個使用者編輯的配置檔案,而是Redis叢集自動存留的Redis配置(狀態,基本的)每當叢集發生變化時,為了在啟動時再次讀取. 這個檔案列出了其他節點在叢集中的狀態、永續性變化等等。這個檔案常常在磁碟上重寫、重新整理作為一些訊息接收的結果.
  • cluster-node-timeout <milliseconds>:Redis叢集中的點不用,被認為失敗節點的的最大等待時間。如果一個主節點在指定的時間段內不可達,就會被他的副節點做失敗處理. 這個引數控制Redis叢集中其它重要事情。尤其,每個節點不能在指定時間內與主節點中的大多數通訊,該節點將會停止查詢。
  • cluster-slave-validity-factor <factor>:如果設定成0,一個副節點總是嘗試FailOver主節點,不管主節點和副節點保持斷開連線多長時間。如果這個值設定為正數,一個最大失聯時間將會被計算出來,node-timeout 乘以這個選項配置的factor, 如果這個節點是副節點,它將不會啟動Failover當失聯超過計算出的指定時間。 例如,如果node-timeoutt設定為5秒,validity factor 設定為10,一個副節點與主節點失去聯絡超過50秒後,副節點將不會嘗試Failover它的主節點。注意,任何不同於0的值將導致叢集不可用,當一個主節點失敗後, 沒有副節點可以替換它。在那個例子中,叢集將返回可用,只要原始的主節點重新加入到叢集中.
  • cluster-migration-barrier <count>:  一個主節點最少副節點連線數目,另一個移動到一個不被任何副節點覆蓋的主節點。閱讀本教程更多關於對於副本遷移恰當分割槽的資訊.
  • cluster-require-full-coverage <yes/no>:如果設定為yes,預設的,如果一定比例的key空間不被任何節點覆蓋,叢集將停止訊息的寫入;如果設定為no,叢集仍然支援查詢服務即使只有一部分key處理請求.

八、建立和使用一個Redis叢集

注意:手工部署一個Redis叢集,學習叢集的特定操作是非常重要的。不管怎樣,如果你想快速的執行一個叢集,跳過這一部分 ,你可以閱讀下一部分建立一個Redis叢集使用create-cluster指令碼。 建立一個叢集,首先我們要有一些以叢集模式執行的空的Redis例項。這基本意味著叢集不是用正常的Redis例項建立的,但是一個特殊的模式需要被配置將使Redis例項擁有叢集指定的屬性和命令。 如下是一個叢集配置檔案最少的配置項:
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
就你所知,使叢集模式工作只需要一個簡單的cluster-enabled指令。每個例項也包含該節點配置資訊被儲存的檔案的路徑,預設是nodes.conf。 該配置檔案不會被手動更改,它僅僅在Redis 叢集例項啟動的時候生成,每次需要的時候更新這個檔案。
注意期望最小叢集工作,要求至少三個主節點。對於你第一次測試,強烈建議你開啟一個6個節點的叢集,包含3個主節點和3個副節點。 就這樣做,進入一個新的目錄,建立如下以埠號命令的目錄:
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
在7000到7005每個目錄下面,建立一個Redis.conf檔案。你配置檔案的模板只要使用上面小例子的內容就可以了,但是要確保替換port 7000 右邊的port  數字和你目錄名字保持一致. 現在把在GitHub上不穩定分支編譯好的可以執行redis-server 原始檔複製到cluster-test目錄下面,最後開啟6個終端標籤在你最喜歡的終端應用上. 向這樣開始每個例項,在每個終端標籤上:
cd 7000
../redis-server ./redis.conf
你可以看見每個例項的日誌,由於nodes.conf檔案不存在,每個節點分配一個新的ID.
[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1
這個ID將被指定的Redis 例項永遠使用,為了讓這個例項在Redis上下文中有一個唯一的名字. 每一個節點都是通過ID來記錄其餘的節點,而不是通過IP地址或埠。 IP地址和埠可以改變,但是這個節點唯一的節點標識在節點生命週期內永遠不會改變。我們把這個標識簡單的稱之為節點ID.

1、建立叢集

現在我們有一個例項在執行,我們需要寫一些對節點有意義的配置來建立叢集。 這個很容易完成通過Redis叢集命令列工具---redis-trib,一個Ruby程式執行在例項上執行特殊的命令來建立Redis叢集、檢查或分片一個已存在的叢集等等。 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
這裡使用的命令是creat,因為我們想建立一個新的Redis叢集。--replicas 1這個選項的意思是我們想要為每個master節點建立一個slave節點,其餘引數是我想建立叢集例項要使用的地址列表。 顯然,叢集是按照我們的要求建立的,3個master節點、3個slave節點。
Redis-trib將會提出一個配置,輸入yes,叢集將會被配置和加入,這意味著,Redis例項將會被引導至互相互動。最後,你將看到一個資訊:
[OK] All 16384 slots covered
上面這條資訊意味著,至少有一個master節點例項服務於16384 slot 中的每一個。

2、使用create-cluster指令碼建立Redis叢集

如果你不想通過手工的配置和執行單獨的例項來建立Redis叢集,就像上面解釋的那樣。這裡有一個更簡單的方法(但是你不必學習相同數量的操作細節)。 只要檢視下Redis分佈的utils/create-cluster 目錄下,這裡有一個叫做create-cluster的指令碼(和包含這個指令碼的目錄名稱相同),它是一個簡單的bash指令碼。為了開始6個節點的叢集(3個master節點,3個slave節點) ,只要輸入如下命令:
1、create-cluster start
2、create-cluster create
第二步的時候,當redis-trib工具想讓你接受redis叢集佈局時,請輸入yes。 現在你能夠和Redis叢集相互作用,第一個節點預設在30001埠啟動。當你完成,使用下面命令停止Redis叢集:
create-cluster stop.
請閱讀目錄下面的README 檔案檢視更多關於執行該指令碼的資訊.

3、玩轉Redis叢集(Playing with the cluster)

在這個階段,Redis叢集的一個問題是缺少客戶端庫的實現。 我知道如下的實現:
redis-rb-cluster is a Ruby implementation written by me (@antirez) as a reference for other languages. It is a simple wrapper around the original redis-rb, implementing the minimal semantics to talk with the cluster efficiently.
redis-py-cluster A port of redis-rb-cluster to Python. Supports majority of redis-py functionality. Is in active development.
The popular Predis has support for Redis Cluster, the support was recently updated and is in active development.
The most used Java client, Jedis recently added support for Redis Cluster, see the Jedis Cluster section in the project README.
StackExchange.Redis offers support for C# (and should work fine with most .NET languages; VB, F#, etc)
thunk-redis offers support for Node.js and io.js, it is a thunk/promise-based redis client with pipelining and cluster.
redis-go-cluster is an implementation of Redis Cluster for the Go language using the Redigo library client as the base client. Implements MGET/MSET via result aggregation.
The redis-cli utility in the unstable branch of the Redis repository at GitHub implements a very basic cluster support when started with the -c switch.
一個簡單的方式來測試你的叢集是使用上面任何一種客戶端或者簡單的命令列工具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"
***************未完待續******************