Redis筆記5-redis高可用方案


一.哨兵機制

  有了主從復制的實現以后,如果想對主服務器進行監控,那么在redis2.6以后提供了一個"哨兵"的機制。顧名思義,哨兵的含義就是監控redis系統的運行狀態。可以啟動多個哨兵,去監控redis數據庫的運行狀態。其主要功能有兩點:

    a、監控所有節點數據庫是否在正常運行。

    b、master數據庫出現故障時,可以自動通過投票機制,從slave節點中選舉新的master,實現將從數據庫轉換為主數據庫的自動切換。

 

一個一主多從的Redis系統中,可以使用多個哨兵進行監控任務以保證系統足夠穩健。此時,不僅哨兵會同時監控主數據庫和從數據庫,哨兵之間也會相互監控。在這里,建議大家哨兵至少部署3個,並且使用奇數個哨兵。

 

Redis的哨兵(sentinel) 系統用於管理多個 Redis 服務器,該系統執行以下三個任務:
       監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常。
       提醒(Notification):當被監控的某個 Redis出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程序發送通知。
      自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級為新的Master, 並讓失效Master的其他Slave改為復制新的Master; 當客戶端試圖連接失效的Master時,集群也會向客戶端返回新Master的地址,使得集群可以使用Master代替失效Master。
哨兵(sentinel) 是一個分布式系統,你可以在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議(gossipprotocols)來接收關於Master是否下線的信息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪個Slave作為新的Master.
每個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送消息,以確認對方是否”活”着,如果發現對方在指定時間(可配置)內未回應,則暫時認為對方已掛(所謂的”主觀認為宕機” Subjective Down,簡稱sdown).
若“哨兵群”中的多數sentinel,都報告某一master沒響應,系統才認為該master"徹底死亡"(即:客觀上的真正down機,Objective Down,簡稱odown),通過一定的vote算法,從剩下的slave節點中,選一台提升為master,然后自動修改相關配置.
雖然哨兵(sentinel) 釋出為一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你可以在啟動一個普通 Redis 服務器時通過給定 --sentinel 選項來啟動哨兵(sentinel).
      哨兵(sentinel) 的一些設計思路和zookeeper非常類似

 

  監控

    sentinel會每秒一次的頻率與之前創建了命令連接的實例發送PING,包括主服務器、從服務器和sentinel實例,以此來判斷當前實例的狀態。down-after-milliseconds時間內PING連接無效,則將該實例視為主觀下線。之后該sentinel會向其他監控同一主服務器的sentinel實例詢問是否也將該服務器視為主觀下線狀態,當超過某quorum后將其視為客觀下線狀態。

    當一個主服務器被某sentinel視為客觀下線狀態后,該sentinel會與其他sentinel協商選出零頭sentinel進行故障轉移工作。每個發現主服務器進入客觀下線的sentinel都可以要求其他sentinel選自己為領頭sentinel,選舉是先到先得。同時每個sentinel每次選舉都會自增配置紀元,每個紀元中只會選擇一個領頭sentinel。如果所有超過一半的sentinel選舉某sentinel領頭sentinel。之后該sentinel進行故障轉移操作。

    如果一個Sentinel為了指定的主服務器故障轉移而投票給另一個Sentinel,將會等待一段時間后試圖再次故障轉移這台主服務器。如果該次失敗另一個將嘗試,Redis Sentinel保證第一個活性(liveness)屬性,如果大多數Sentinel能夠對話,如果主服務器下線,最后只會有一個被授權來故障轉移。 同時Redis Sentinel也保證安全(safety)屬性,每個Sentinel將會使用不同的配置紀元來故障轉移同一台主服務器。

    故障轉移

    首先是從主服務器的從服務器中選出一個從服務器作為新的主服務器。選點的依據依次是:網絡連接正常->5秒內回復過INFO命令->10*down-after-milliseconds內與主連接過的->從服務器優先級->復制偏移量->運行id較小的。選出之后通過slaveif no ont將該從服務器升為新主服務器。

    通過slaveof ip port命令讓其他從服務器復制該信主服務器。

    最后當舊主重新連接后將其變為新主的從服務器。注意如果客戶端與就主服務器分隔在一起,寫入的數據在恢復后由於舊主會復制新主的數據會造成數據丟失。

    故障轉移成功后會通過發布訂閱連接廣播新的配置信息,其他sentinel收到后依據配置紀元更大來更新主服務器信息。Sentinel保證第二個活性屬性:一個可以相互通信的Sentinel集合會統一到一個擁有更高版本號的相同配置上。    

我們可以看到哨兵機制是有缺點的:

  1.主從服務器的數據要經常進行主從復制,這樣造成性能下降。

  2.當主服務器宕機后,從服務器切換成主服務器的那段時間,服務是不能用的。

 

二.一致性哈希

    • 普通的HASH算法的缺點:
      在使用Redis集群的時候,如果直接使用HASH算法 hash(key) % length,當緩存服務器變化時(宕機或新增節點),length字段變化,導致所有緩存的數據需要重新進行HASH運算,這樣就導致原來的哪些數據訪問不到了。而這段時間如果訪問量上升了,容易引起服務器雪崩。因此,引入了一致性哈希
    • 一致性哈希:通過對2^32取模的方式,保證了在增加/刪除緩存服務器的情況下,其他緩存服務器的緩存仍然可用,從而不引起雪崩問題。

 

 2^32想象成一個圓,就像鍾表一樣,示意圖如下:

 

     

圓環的正上方的點代表0,0點的右側的第一個點代表1,以此類推,2,3,4,..........直到2^32-1,也就是說0的左側第一個點代表2^32-1,我們把這個由2^32個點組成的圓環成為Hash環。

假設現在我們有4台服務器,通過Hash(服務器的IP地址)% 2^32得到服務器映射到Hash環上的位置。那么我要要緩存的數據對象怎么判斷還存在哪些服務器上呢?

一致性哈希算法通過將緩存服務器和被緩存對象都映射到Hash環上以后,從被緩存對象的位置出發,沿順時針方向遇到的第一個服務器,就是當前對象將要被緩存於的服務器,由於被緩存對象

與服務器hash后的值是固定的,所以在服務器不變的情況下,一個對象必定緩存在一個固定的服務器上,那么,當再次訪問這對象時,只要再次使用相同的算法計算即可算出這對象被緩存到哪個服務器上。如下圖:

實際應用中一致性哈希還可能出現極端狀態如下圖:

如果服務器和對象映射成上圖這樣子,那么被緩存的對象很有可能大部分集中緩存在某一台服務器上。這種現象稱為Hash環的偏斜。

那如何解決該現象呢

我們可以通過虛擬節點的方式解決Hash環的偏移。


如果想要均衡的將緩存分布到這三台服務器上,最好能讓這三台服務器的盡量多的,均勻的出現在Hash環上,但是,真實的服務器資源只有3台,那么如何憑空的讓他們多起來呢?

做法就是既然沒有多余的真正的物理服務器節點,我們就可能將現有的物理節點通過虛擬的方法復制出來,而被復制出來的節點被稱為“虛擬節點”,加入虛擬節點后的Hash環如下圖:

 

 

如果其中一個節點宕機了,一致性Hash能解決容災問題嗎?

明顯可以的,我們假設有5台服務器,有a,f,c對象映射在A服務器上,r,t,v對象映射在E服務器上。如下圖:

假如此時E服務器宕機了,其他服務器的對象仍然能被命中,因為對象的映射到服務器的位置已經固定了,不會出現因為宕機而讓對象找不到。而宕機E上的對象會在下次容災分配的時候,會把r,t,v這些對象重新分配到就近的服務器上,如下圖:

三.Redis官方集群方案 Redis Cluster

Redis在3.0版正式引入了集群這個特性。Redis集群是一個分布式(distributed)、容錯(fault-tolerant)的 Redis內存K/V服務, 集群可以使用的功能是普通單機 Redis 所能使用的功能的一個子集(subset),比如Redis集群並不支持處理多個keys的命令,因為這需要在不同的節點間移動數據,從而達不到像Redis那樣的性能,在高負載的情況下可能會導致不可預料的錯誤。

Redis集群的幾個重要特征:
(1). Redis 集群的分片特征在於將鍵空間分拆了16384個槽位,每一個節點負責其中一些槽位。
(2). Redis提供一定程度的可用性,可以在某個節點宕機或者不可達的情況下繼續處理命令.
(3). Redis 集群中不存在中心(central)節點或者代理(proxy)節點, 集群的其中一個主要設計目標是達到線性可擴展性(linear scalability)。

 

Retis Cluster的架構圖,如下:

 

 

Redis Cluster特點如下:

  • 所有的節點相互連接;
  • 集群消息通信通過集群總線通信,,集群總線端口大小為客戶端服務端口+10000,這個10000是固定值;
  • 節點與節點之間通過二進制協議進行通信;
  • 客戶端和集群節點之間通信和通常一樣,通過文本協議進行;
  • 集群節點不會代理查詢;

 

Redis Cluster分區實現原理

槽(slot)概念

Redis Cluster中有一個16384長度的槽的概念,他們的編號為0、1、2、3……16382、16383。這個槽是一個虛擬的槽,並不是真正存在的。正常工作的時候,Redis Cluster中的每個Master節點都會負責一部分的槽,當有某個key被映射到某個Master負責的槽,那么這個Master負責為這個key提供服務,至於哪個Master節點負責哪個槽,這是可以由用戶指定的,也可以在初始化的時候自動生成(redis-trib.rb腳本)。這里值得一提的是,在Redis Cluster中,只有Master才擁有槽的所有權,如果是某個Master的slave,這個slave只負責槽的使用,但是沒有所有權。Redis Cluster怎么知道哪些槽是由哪些節點負責的呢?某個Master又怎么知道某個槽自己是不是擁有呢?

 

位序列結構

Master節點維護着一個16384/8字節的位序列,Master節點用bit來標識對於某個槽自己是否擁有。比如對於編號為1的槽,Master只要判斷序列的第二位(索引從0開始)是不是為1即可。

如上面的序列,表示當前Master擁有編號為1,134的槽。集群同時還維護着槽到集群節點的映射,是由長度為16384類型為節點的數組實現的,槽編號為數組的下標,數組內容為集群節點,這樣就可以很快地通過槽編號找到負責這個槽的節點。位序列這個結構很精巧,即不浪費存儲空間,操作起來又很便捷。

 

 

故障容忍度

 

(1)心跳和gossip消息

Redis Cluster持續的交換PING和PONG數據包。這兩種數據包的數據結構相同,都包含重要的配置信息,唯一的不同是消息類型字段。PING和PONG數據包統稱為心跳數據包。

每個節點在每一秒鍾都向一定數量的其它節點發送PING消息,這些節點應該向發送PING的節點回復一個PONG消息。節點會盡可能確保擁有每個其它節點在NOTE_TIMEOUT/2秒時間內的最新信息,否則會發送一個PING消息,以確定與該節點的連接是否正常。

假定一個Cluster有301個節點,NOTE_TIMEOUT為60秒,那么每30秒每個節點至少發送300個PING,即每秒10個PING, 整個Cluster每秒發送10x301=3010個PING。這個數量級的流量不應該會造成網絡負擔。

 

(2)故障檢測。

Redis Cluster的故障檢測用於檢測一個master節點何時變得不再有效,即不能提供服務,從而應該讓slave節點提升為master節點。如果提升失敗,則整個Cluster失效,不再接受客戶端的服務請求。

當一個節點A向另外一個節點B發送了PING消息之后,經過NODE_TIMEOUT秒時間之后仍然沒有收到PONG應答,則節點A認為節點B失效,節點A將為該節點B設置PFAIL標志。

在 NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT時間內,當Cluster中大多數節點認為節點B失效,即設置PFAIL標志時,這個Cluster認為節點B真的失效了,此時節點A將為節點B設置FAIL標志,並向所有節點發送FAIL消息。

在一些特定情況下,擁有FAIL標志的節點,也可以清除掉FAIL標志。

 

Redis Cluster故障檢測機制最終應該讓所有節點都一致同意某個節點處於某個確定的狀態。如果發生這樣的情況少數節點確信某個節點為FAIL,同時有少數節點確認某個節點為非FAIL,則Redis Cluster最終會處於一個確定的狀態:

情況1:最終大多數節點認為該節點FAIL,該節點最終實際為FAIL。

情況2:最終在N x NODE_TIMEOUT時間內,仍然只有少數節點將給節點標記為FAIL,此時最終會清除這個節點的FAIL標志。

 

Redis投票容錯如下圖所示:

 

 重定向客戶端

文章開始講到,Redis Cluster並不會代理查詢,那么如果客戶端訪問了一個key並不存在的節點,這個節點是怎么處理的呢?比如我想獲取key為msg的值,msg計算出來的槽編號為254,當前節點正好不負責編號為254的槽,那么就會返回客戶端下面信息:

表示客戶端想要的254槽由運行在IP為127.0.0.1,端口為6381的Master實例服務。如果根據key計算得出的槽恰好由當前節點負責,則當期節點會立即返回結果。這里明確一下,沒有代理的Redis Cluster可能會導致客戶端兩次連接急群中的節點才能找到正確的服務,推薦客戶端緩存連接,這樣最壞的情況是兩次往返通信。

 

 slots配置傳播

Redis Cluster采用兩種方式進行各個master節點的slots配置信息的傳播。所謂slots配置信息,即master負責存儲哪幾個slots。

(1)心跳消息。

在PING/PONG消息中包含了所負責的slots配置信息。

(2)UPDATE消息。

當一個節點收到PING/PONG消息后,如果發現發送者的世代小於自己的世代,則向其發送UPDATE消息,來更新其有可能已經過時的slots配置信息。如果發現發送者的世代大於自己的世代,則用消息中的slots配置信息更新自己的slots配置信息。 

重新分片

Redis Cluster的Resharding是指在Cluster內的節點之間轉移slots中的鍵數據,一個slot之前由某個節點負責,在Resharding之后,可能由另外一個節點負責。

 復制遷移

Redis Cluster在節點失效時,可能進行自動的slave節點重新配置,修改了Cluster中部分節點的master-slave復制關系,即復制遷移。

假定場景:

Cluster中有三個master節點:A、B、C。A有1個slave節點A1,B有1個slave節點B1,C有2個slave節點C1和C2。Aj節點失效了,將A1節點提升為master節點。

考慮不進行自動的slave節點的復制遷移:

如果A失效了,則會將唯一的slave節點A1提升為master節點,但是它沒有slave節點。這時如果A1節點又失效了,則原來A節點負責的slots將失效,導致整個cluster不能正常工作。

考慮進行自動的slave節點的復制遷移:

如果A節點失效了,將唯一的slave節點A1提升為master節點,由於它沒有slave節點,此時發現C節點有2個slave節點,將其中的C2節點重新配置為A1節點的子節點。這時,Cluster中每個master節點至少有1個slave節點。如果A1節點失效,可將C2節點提升為master。這樣的結果是提高了整個cluster的可用性。

 

架構細節:

(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬.

(2)節點的fail是通過集群中超過半數的節點檢測失效時才生效.

(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可

(4)redis-cluster把所有的物理節點映射到[0-16383]slot上,cluster 負責維護node<->slot<->value

Redis 集群中內置了 16384 個哈希槽,當需要在 Redis 集群中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,然后把結果對 16384 求余數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM