Redis Cluster原理

今晚打老虎嗎發表於2019-04-08

前言

本來最近打算學習Unix網路程式設計,但是專案中專案中用到了Redis Cluster,自己對Redis叢集這方面並不是很熟悉,所以打算花點時間來系統的學習一下Redis,Redis主要分四個部分;
1.資料結構與物件
2.單機資料庫實現
3.多機資料庫實現
4.獨立功能的實現
本文先簡單介紹下Redis 的叢集實現,後續會針對上面的在進行詳解;

Redis 叢集介紹

Redis因為具有豐富的資料結構和超高額效能以及簡單的協議,使其能夠很好的作用為資料庫的上游。但是當資料量變大的時候(如資料達到千萬級別時),會受限以多個地方,單機記憶體有限、單點問題、動態擴容問題等。
為了解決上面的問題,Redis的叢集方案就顯得比較重要了。使用Redis叢集通常有三個途徑;

  • 官方提供的 Redis Cluster
  • 通過Proxy分片
  • 客戶端分片(Smart Client)

本文主要介紹一下官方提供的 Redis Cluster,Redis Cluster 是在Redis 3.0開始支援的,3.0主要是更新了多機方面的功能,在5.0之前Redis Cluster 主要是採用 Redis提供的 redis-trib.rb(Ruby實現的指令碼,需要安裝相應的依賴環境)這個管理工具。5.0版本開始支援 --cluster 引數來進行管理。

Redis Cluster 資料分片

Redis Cluster 並沒有使用一致性hash,而是引用了一個叫雜湊槽的概念。 Redis Cluster 中有16384個雜湊槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽。叢集的每個節點分配一部分的雜湊槽,這樣有一點很方便就是在增加和移除的時候,只需要分配相應的雜湊槽就可以了。
在單命令執行下和單機Redis並無區別,客戶端寫入命令,由Redis Cluster計算當前key屬於哪個槽點然後返回相應資料。但是多命令的情況下(如求集合的交集)有可能多個key不在同一個槽點裡,可以採用{}來設定需要多命令執行的key。{key}:xxx它只對括號裡面的進行hash操作,所以可以保證在同一個雜湊槽中。

Redis Cluster 埠

Redis Cluster 的每個節點都需要維護兩個TCP埠,用於為Redis提供服務的普通埠,例如6370,加上通過向資料埠新增10000獲得的埠,如16379。
第二個埠是用於叢集匯流排,使用二進位制節點到節點的通訊通道(gossip 協議)。匯流排埠的偏移量是固定的,始終為10000。節點使用的叢集匯流排主要用來進行故障檢測、資料更新、故障轉移授權等 。為了保證叢集的正常使用,客戶端永不應該嘗試與叢集匯流排通訊,始終使用正常的埠通訊。防火牆要開啟這兩個埠,否則Redis節點將無法通訊。

Redis Cluster 故障恢復問題

為了提高可用性,Redis 採用了主從模型,其中每一個節點擁有一個master N個slave,如果其中一個節點的 master出現問題,它的一個slave會被選舉為master節點。如果某個節點的master和slave全部掛掉,那麼這個Redis Cluster將無法服務。其中還有一點需要注意,選舉的時候需要有其它一半以上的master節點參加。如果沒有一半以上的master節點存活參與選舉則slave不會被選舉為master。如兩個master服務,其中一個掛掉,掛掉服務的slave是不會被選舉成為master的,因為存活的master不到一半以上。或者是master剛好分配在一臺伺服器上,這個伺服器掛掉,即使他的slave存在,叢集也是沒辦法將slave節點選舉為master節點做到自動故障轉移的。所以我們在分配節點的時候一定要注意合理分配,最好自己分配(預設自動分配)。

Redis Cluster 強一致性

首先我們要知道Redis Cluster 並不提供強一致性。這意味著在某種條件下,Redis 會造成資料丟失。主要原因是因為Redis Cluster 採用了非同步複製:

  1. 客戶端向主機寫入
  2. 主機向客戶端回覆確認
  3. 主機通知該節點的slave裝置

基於這種情況,如果出現了上面的故障問題,主機在崩潰後沒來得及通知其slave服務,並且這個slave被選為master,那麼這個資料則會永久丟失。
Redis Cluster 也支援同步複製,通過WAIT命令實現,這樣會大大的降低資料丟失的可能性。但即使是同步複製,也不可能保證強一致性。在更復雜的情況下總是可以實現失敗場景,無法接收寫入的slave被選為master。
還有另一個值得注意的情況是,Redis Cluster將丟失寫入,這種情況發生在網路分割槽中 。客戶端向節點請求是有最大時間限制(cluster-node-timeout) 不推薦用redis事務機制,因為採用了分片處理。如果一個事物中涉及到多個key的操作的話,這麼多個key不一定都儲存在同一個節點上。
Redis 分散式鎖
如果確實需要強一致性的話可以考慮採用分散式鎖:

  • 實現邏輯
    在獲取鎖的時候,使用SETNX加鎖,並使用EXPIRE命令為鎖新增一個超時時間,超過該事件自動釋放鎖,鎖的value值會隨機生成一個UUID,釋放鎖的時候,通過UUID判斷是不是該鎖,若是該鎖,則釋放。如果在執行EXPIRE之前服務掛掉了那麼這個鎖就永遠得不到釋放了。可以用SET命令同時設定SETNX和EXPIRE
  • 併發競爭問題
    如果不要求執行順序則讓大家去搶鎖,搶到了就進行操作。如果需要順序,可以考慮設定一個時間戳搶到的節點判斷當前時間戳是否屬於自己,若屬於自己在進行操作。

相關文章