1. 程式人生 > >Redis叢集模式

Redis叢集模式

此文由多處轉載,自行組織,水平有限,有錯誤,望大家指正。

現在越來越多的專案都會利用到redis,多例項redis服務比單例項要複雜的多,這裡面涉及到定位、容錯、擴容等技術問題。我們常用sharding技術來對此進行管理,其叢集模式主要有以下幾種方式:

  1. 主從複製
  2. 哨兵模式
  3. Redis官方 Cluster叢集模式(服務端sharding)
  4. Jedis sharding叢集(客戶端sharding)
  5. 利用中介軟體代理

我們現看看主從複製、和哨兵模式,這是其它叢集的基礎配置.

主從複製(Master-Slave Replication)

實現主從複製(Master-Slave Replication)的工作原理:Slave從節點服務啟動並連線到Master之後,它將主動傳送一個SYNC命令。Master服務主節點收到同步命令後將啟動後臺存檔程序,同時收集所有接收到的用於修改資料集的命令,在後臺程序執行完畢後,Master將傳送整個資料庫檔案到Slave,以完成一次完全同步。而Slave從節點服務在接收到資料庫檔案資料之後將其存檔並載入到記憶體中。此後,Master主節點繼續將所有已經收集到的修改命令,和新的修改命令依次傳送給Slaves,Slave將在本次執行這些資料修改命令,從而達到最終的資料同步。

如果Master和Slave之間的連結出現斷連現象,Slave可以自動重連Master,但是在連線成功之後,一次完全同步將被自動執行。

主從複製配置
  • 修改從節點的配置檔案:slaveof masterip masterport
  • 如果設定了密碼,就要設定:masterauth master-password
主從模式的優缺點

優點:

  • 同一個Master可以同步多個Slaves。
  • Slave同樣可以接受其它Slaves的連線和同步請求,這樣可以有效的分載Master的同步壓力。因此我們可以將Redis的Replication架構視為圖結構。
  • Master Server是以非阻塞的方式為Slaves提供服務。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求。
  • Slave Server同樣是以非阻塞的方式完成資料同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的資料
  • 為了分載Master的讀操作壓力,Slave伺服器可以為客戶端提供只讀操作的服務,寫服務仍然必須由Master來完成。即便如此,系統的伸縮性還是得到了很大的提高。
  • Master可以將資料儲存操作交給Slaves完成,從而避免了在Master中要有獨立的程序來完成此操作。
  • 支援主從複製,主機會自動將資料同步到從機,可以進行讀寫分離。

缺點:

  • Redis不具備自動容錯和恢復功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或者手動切換前端的IP才能恢復。
  • 主機宕機,宕機前有部分資料未能及時同步到從機,切換IP後還會引入資料不一致的問題,降低了系統的可用性。
  • Redis的主從複製採用全量複製,複製過程中主機會fork出一個子程序對記憶體做一份快照,並將子程序的記憶體快照儲存為檔案傳送給從機,這一過程需要確保主機有足夠多的空餘記憶體。若快照檔案較大,對叢集的服務能力會產生較大的影響,而且複製過程是在從機新加入叢集或者從機和主機網路斷開重連時都會進行,也就是網路波動都會造成主機和從機間的一次全量的資料複製,這對實際的系統運營造成了不小的麻煩。
  • Redis較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜。為避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。

其實redis的主從模式很簡單,在實際的生產環境中是很少使用的,我也不建議在實際的生產環境中使用主從模式來提供系統的高可用性,之所以不建議使用都是由它的缺點造成的,在資料量非常大的情況,或者對系統的高可用性要求很高的情況下,主從模式也是不穩定的。

哨兵模式

該模式是從Redis的2.6版本開始提供的,但是當時這個版本的模式是不穩定的,直到Redis的2.8版本以後,這個哨兵模式才穩定下來,無論是主從模式,還是哨兵模式,這兩個模式都有一個問題,不能水平擴容,並且這兩個模式的高可用特性都會受到Master主節點記憶體的限制。

Sentinel(哨兵)程序是用於監控redis叢集中Master主伺服器工作的狀態,在Master主伺服器發生故障的時候,可以實現Master和Slave伺服器的切換,保證系統的高可用。

Sentinel(哨兵)程序的作用
  1. 監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常。
  2. 提醒(Notification):當被監控的某個Redis節點出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程式傳送通知。
  3. 自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級為新的Master, 並讓失效Master的其他Slave改為複製新的Master;當客戶端試圖連線失效的Master時,叢集也會向客戶端返回新Master的地址,使得叢集可以使用現在的Master替換失效Master。Master和Slave伺服器切換後,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置檔案的內容都會發生相應的改變,即,Master主伺服器的redis.conf配置檔案中會多一行slaveof的配置,sentinel.conf的監控目標會隨之調換。
Sentinel(哨兵)程序的工作方式
  1. 每個Sentinel(哨兵)程序以每秒鐘一次的頻率向整個叢集中的Master主伺服器,Slave從伺服器以及其他Sentinel(哨兵)程序傳送一個 PING 命令。
  2. 如果一個例項(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個例項會被 Sentinel(哨兵)程序標記為主觀下線(SDOWN)
  3. 如果一個Master主伺服器被標記為主觀下線(SDOWN),則正在監視這個Master主伺服器的所有 Sentinel(哨兵)程序要以每秒一次的頻率確認Master主伺服器的確進入了主觀下線狀態
  4. 當有足夠數量的 Sentinel(哨兵)程序(大於等於配置檔案指定的值)在指定的時間範圍內確認Master主伺服器進入了主觀下線狀態(SDOWN), 則Master主伺服器會被標記為客觀下線(ODOWN)
  5. 在一般情況下, 每個 Sentinel(哨兵)程序會以每 10 秒一次的頻率向叢集中的所有Master主伺服器、Slave從伺服器傳送 INFO 命令。
  6. 當Master主伺服器被 Sentinel(哨兵)程序標記為客觀下線(ODOWN)時,Sentinel(哨兵)程序向下線的 Master主伺服器的所有 Slave從伺服器傳送 INFO 命令的頻率會從 10 秒一次改為每秒一次。
  7. 若沒有足夠數量的 Sentinel(哨兵)程序同意 Master主伺服器下線, Master主伺服器的客觀下線狀態就會被移除。若 Master主伺服器重新向 Sentinel(哨兵)程序傳送 PING 命令返回有效回覆,Master主伺服器的主觀下線狀態就會被移除。
哨兵模式的優缺點

優點:

  • 哨兵叢集模式是基於主從模式的,所有主從的優點,哨兵模式同樣具有。
  • 主從可以切換,故障可以轉移,系統可用性更好。
  • 哨兵模式是主從模式的升級,系統更健壯,可用性更高。

缺點:

  • Redis較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜。為避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。
  • 配置複雜

Redis官方 Cluster叢集模式

Redis Cluster是一種伺服器Sharding技術,3.0版本開始正式提供。
這裡寫圖片描述

在這個圖中,每一個藍色的圈都代表著一個redis的伺服器節點。它們任何兩個節點之間都是相互連通的。客戶端可以與任何一個節點相連線,然後就可以訪問叢集中的任何一個節點。對其進行存取和其他操作。

Redis叢集資料分片

在redis的每一個節點上,都有這麼兩個東西,一個是插槽(slot)可以理解為是一個可以儲存兩個數值的一個變數這個變數的取值範圍是:0-16383。還有一個就是cluster我個人把這個cluster理解為是一個叢集管理的外掛。當我們的存取的key到達的時候,redis會根據crc16的演算法得出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作。
這裡寫圖片描述

還有就是因為如果叢集的話,是有好多個redis一起工作的,那麼,就需要這個叢集不是那麼容易掛掉,所以呢,理論上就應該給叢集中的每個節點至少一個備用的redis服務。這個備用的redis稱為從節點(slave)。那麼這個叢集是如何判斷是否有某個節點掛掉了呢?

首先要說的是,每一個節點都存有這個叢集所有主節點以及從節點的資訊。

它們之間通過互相的ping-pong判斷是否節點可以連線上。如果有一半以上的節點去ping一個節點的時候沒有迴應,叢集就認為這個節點宕機了,然後去連線它的備用節點。如果某個節點和所有從節點全部掛掉,我們叢集就進入faill狀態。還有就是如果有一半以上的主節點宕機,那麼我們叢集同樣進入發力了狀態。這就是我們的redis的投票機制,具體原理如下圖所示:
這裡寫圖片描述

  • 投票過程是叢集中所有master參與,如果半數以上master節點與master節點通訊超時(cluster-node-timeout),認為當前master節點掛掉.
  • 什麼時候整個叢集不可用(cluster_state:fail)?
    1. 如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完整時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗.
    2. 如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完整時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗.

Redis 3.0的叢集方案有以下兩個問題。

  1. 一個Redis例項具備了“資料儲存”和“路由重定向”,完全去中心化的設計。這帶來的好處是部署非常簡單,直接部署Redis就行,不像Codis有那麼多的元件和依賴。但帶來的問題是很難對業務進行無痛的升級,如果哪天Redis叢集出了什麼嚴重的Bug,就只能回滾整個Redis叢集。
  2. 對協議進行了較大的修改,對應的Redis客戶端也需要升級。升級Redis客戶端後誰能確保沒有Bug?而且對於線上已經大規模執行的業務,升級程式碼中的Redis客戶端也是一個很麻煩的事情。

Redis Cluster是Redis 3.0以後才正式推出,時間較晚,目前能證明在大規模生產環境下成功的案例還不是很多,需要時間檢驗。

Jedis sharding叢集

Redis Sharding可以說是在Redis cluster出來之前業界普遍的採用方式,其主要思想是採用hash演算法將儲存資料的key進行hash雜湊,這樣特定的key會被定為到特定的節點上。

這裡寫圖片描述

慶幸的是,Java Redis客戶端驅動Jedis已支援Redis Sharding功能,即ShardedJedis以及結合快取池的ShardedJedisPool

Jedis的Redis Sharding實現具有如下特點:

  1. 採用一致性雜湊演算法,將key和節點name同時hashing,然後進行對映匹配,採用的演算法是MURMUR_HASH。採用一致性雜湊而不是採用簡單類似雜湊求模對映的主要原因是當增加或減少節點時,不會產生由於重新匹配造成的rehashing。一致性雜湊隻影響相鄰節點key分配,影響量小。
  2. 為了避免一致性雜湊隻影響相鄰節點造成節點分配壓力,ShardedJedis會對每個Redis節點根據名字(沒有,Jedis會賦予預設名字)會虛擬化出160個虛擬節點進行雜湊。根據權重weight,也可虛擬化出160倍數的虛擬節點。用虛擬節點做對映匹配,可以在增加或減少Redis節點時,key在各Redis節點移動再分配更均勻,而不是隻有相鄰節點受影響。
  3. ShardedJedis支援keyTagPattern模式,即抽取key的一部分keyTag做sharding,這樣通過合理命名key,可以將一組相關聯的key放入同一個Redis節點,這在避免跨節點訪問相關資料時很重要。

當然,Redis Sharding這種輕量靈活方式必然在叢集其它能力方面做出妥協。比如擴容,當想要增加Redis節點時,儘管採用一致性雜湊,畢竟還是會有key匹配不到而丟失,這時需要鍵值遷移。
作為輕量級客戶端sharding,處理Redis鍵值遷移是不現實的,這就要求應用層面允許Redis中資料丟失或從後端資料庫重新載入資料。但有些時候,擊穿快取層,直接訪問資料庫層,會對系統訪問造成很大壓力。

利用中介軟體代理

中介軟體的作用是將我們需要存入redis中的資料的key通過一套演算法計算得出一個值。然後根據這個值找到對應的redis節點,將這些資料存在這個redis的節點中。

常用的中介軟體有這幾種

  • Twemproxy
  • Codis
  • nginx

具體用法就不贅述了,可以自行百度。

總結

  1. 客戶端分片(sharding)需要客戶端維護分片演算法,這是一種靜態的分片方案,需要增加或者減少Redis例項的數量,需要手工調整分片的程式。
  2. 利用中介軟體的情況則會影響到redis的效能,具體看中介軟體而定,畢竟所有請求都要經過中介軟體一層過濾
  3. 官方提供方案,現時點成功案例不多。