天天看點

百度面試官:談談你對Redis高可用的了解

作者:Java靈風

前言

我們在項目中使用redis,肯定不會是單點部署Redis服務的。因為單點部署一旦當機,就不可用了。為了實作高可用,通常的做法是,将資料庫複制多個副本以部署在不同的伺服器上,其中一台挂了也可以繼續提供服務。Redis 實作高可用有三種部署模式:主從模式,哨兵模式,叢集模式。

1.主從模式

主從模式中,Redis部署了多台機器,有主節點,負責讀寫操作,有從節點,隻負責讀操作。從節點的資料來自主節點,實作原理就是主從複制機制

主從複制包括全量複制,增量複制兩種。一般當slave第一次啟動連接配接master,或者認為是第一次連接配接,就采用全量複制,全量複制流程如下:

百度面試官:談談你對Redis高可用的了解
  • 1.slave發送sync指令到master。
  • 2.master接收到SYNC指令後,執行bgsave指令,生成RDB全量檔案。
  • 3.master使用緩沖區,記錄RDB快照生成期間的所有寫指令。
  • 4.master執行完bgsave後,向所有slave發送RDB快照檔案。
  • 5.slave收到RDB快照檔案後,載入、解析收到的快照。
  • 6.master使用緩沖區,記錄RDB同步期間生成的所有寫的指令。
  • 7.master快照發送完畢後,開始向slave發送緩沖區中的寫指令;
  • 8.salve接受指令請求,并執行來自master緩沖區的寫指令

redis2.8版本之後,已經使用psync來替代sync,因為sync指令非常消耗系統資源,psync的效率更高。

slave與master全量同步之後,master上的資料,如果再次發生更新,就會觸發增量複制。

當master節點發生資料增減時,就會觸發replicationFeedSalves()函數,接下來在 Master節點上調用的每一個指令會使用replicationFeedSlaves()來同步到Slave節點。執行此函數之前呢,master節點會判斷使用者執行的指令是否有資料更新,如果有資料更新的話,并且slave節點不為空,就會執行此函數。這個函數作用就是:把使用者執行的指令發送到所有的slave節點,讓slave節點執行。流程如下:

百度面試官:談談你對Redis高可用的了解

2.哨兵模式

主從模式中,一旦主節點由于故障不能提供服務,需要人工将從節點晉升為主節點,同時還要通知應用方更新主節點位址。顯然,多數業務場景都不能接受這種故障處理方式。Redis從2.8開始正式提供了Redis Sentinel(哨兵)架構來解決這個問題。

哨兵模式,由一個或多個Sentinel執行個體組成的Sentinel系統,它可以監視所有的Redis主節點和從節點,并在被監視的主節點進入下線狀态時,自動将下線主伺服器屬下的某個從節點更新為新的主節點。但是呢,一個哨兵程序對Redis節點進行監控,就可能會出現問題(單點問題),是以,可以使用多個哨兵來進行監控Redis節點,并且各個哨兵之間還會進行監控。

百度面試官:談談你對Redis高可用的了解

Sentinel哨兵模式

簡單來說,哨兵模式就三個作用:

  • 發送指令,等待Redis伺服器(包括主伺服器和從伺服器)傳回監控其運作狀态;
  • 哨兵監測到主節點當機,會自動将從節點切換成主節點,然後通過釋出訂閱模式通知其他的從節點,修改配置檔案,讓它們切換主機;
  • 哨兵之間還會互相監控,進而達到高可用。

故障切換的過程是怎樣的呢

假設主伺服器當機,哨兵1先檢測到這個結果,系統并不會馬上進行 failover 過程,僅僅是哨兵1主觀的認為主伺服器不可用,這個現象成為主觀下線。當後面的哨兵也檢測到主伺服器不可用,并且數量達到一定值時,那麼哨兵之間就會進行一次投票,投票的結果由一個哨兵發起,進行 failover 操作。切換成功後,就會通過釋出訂閱模式,讓各個哨兵把自己監控的從伺服器實作切換主機,這個過程稱為客觀下線。這樣對于用戶端而言,一切都是透明的。

哨兵的工作模式如下:

  1. 每個Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他Sentinel執行個體發送一個 PING指令。
  2. 如果一個執行個體(instance)距離最後一次有效回複 PING 指令的時間超過 down-after-milliseconds 選項所指定的值, 則這個執行個體會被 Sentinel标記為主觀下線。
  3. 如果一個Master被标記為主觀下線,則正在監視這個Master的所有 Sentinel 要以每秒一次的頻率确認Master的确進入了主觀下線狀态。
  4. 當有足夠數量的 Sentinel(大于等于配置檔案指定的值)在指定的時間範圍内确認Master的确進入了主觀下線狀态, 則Master會被标記為客觀下線。
  5. 在一般情況下, 每個 Sentinel 會以每10秒一次的頻率向它已知的所有Master,Slave發送 INFO 指令。
  6. 當Master被 Sentinel 标記為客觀下線時,Sentinel 向下線的 Master 的所有 Slave 發送 INFO 指令的頻率會從 10 秒一次改為每秒一次
  7. 若沒有足夠數量的 Sentinel同意Master已經下線, Master的客觀下線狀态就會被移除;若Master 重新向 Sentinel 的 PING 指令傳回有效回複, Master 的主觀下線狀态就會被移除。

3. Cluster叢集模式

哨兵模式基于主從模式,實作讀寫分離,它還可以自動切換,系統可用性更高。但是它每個節點存儲的資料是一樣的,浪費記憶體,并且不好線上擴容。是以,Cluster叢集應運而生,它在Redis3.0加入的,實作了Redis的分布式存儲。對資料進行分片,也就是說每台Redis節點上存儲不同的内容,來解決線上擴容的問題。并且,它也提供複制和故障轉移的功能。

3.1 Cluster叢集節點的通訊

一個Redis叢集由多個節點組成,各個節點之間是怎麼通信的呢?通過Gossip協定!

Redis Cluster叢集通過Gossip協定進行通信,節點之前不斷交換資訊,交換的資訊内容包括節點出現故障、新節點加入、主從節點變更資訊、slot資訊等等。常用的Gossip消息分為4種,分别是:ping、pong、meet、fail。

百度面試官:談談你對Redis高可用的了解

meet消息:通知新節點加入。消息發送者通知接收者加入到目前叢集,meet消息通信正常完成後,接收節點會加入到叢集中并進行周期性的ping、pong消息交換。

ping消息:叢集内交換最頻繁的消息,叢集内每個節點每秒向多個其他節點發送ping消息,用于檢測節點是否線上和交換彼此狀态資訊。

pong消息:當接收到ping、meet消息時,作為響應消息回複給發送方确認消息正常通信。pong消息内部封裝了自身狀态資料。節點也可以向叢集内廣播自身的pong消息來通知整個叢集對自身狀态進行更新。

fail消息:當節點判定叢集内另一個節點下線時,會向叢集内廣播一個fail消息,其他節點接收到fail消息之後把對應節點更新為下線狀态。

特别的,每個節點是通過叢集總線(cluster bus) 與其他的節點進行通信的。通訊時,使用特殊的端口号,即對外服務端口号加10000。例如如果某個node的端口号是6379,那麼它與其它nodes通信的端口号是 16379。nodes 之間的通信采用特殊的二進制協定。

3.2 Hash Slot插槽算法

既然是分布式存儲,Cluster叢集使用的分布式算法是一緻性Hash嘛?并不是,而是Hash Slot插槽算法。

插槽算法把整個資料庫被分為16384個slot(槽),每個進入Redis的鍵值對,根據key進行散列,配置設定到這16384插槽中的一個。使用的哈希映射也比較簡單,用CRC16算法計算出一個16 位的值,再對16384取模。資料庫中的每個鍵都屬于這16384個槽的其中一個,叢集中的每個節點都可以處理這16384個槽。

叢集中的每個節點負責一部分的hash槽,比如目前叢集有A、B、C個節點,每個節點上的哈希槽數 =16384/3,那麼就有:

  • 節點A負責0~5460号哈希槽
  • 節點B負責5461~10922号哈希槽
  • 節點C負責10923~16383号哈希槽

3.3 Redis Cluster叢集

Redis Cluster叢集中,需要確定16384個槽對應的node都正常工作,如果某個node出現故障,它負責的slot也會失效,整個叢集将不能工作。

是以為了保證高可用,Cluster叢集引入了主從複制,一個主節點對應一個或者多個從節點。當其它主節點 ping 一個主節點 A 時,如果半數以上的主節點與 A 通信逾時,那麼認為主節點 A 當機了。如果主節點當機時,就會啟用從節點。

在Redis的每一個節點上,都有兩個玩意,一個是插槽(slot),它的取值範圍是0~16383。另外一個是cluster,可以了解為一個叢集管理的插件。當我們存取的key到達時,Redis 會根據CRC16算法得出一個16 bit的值,然後把結果對16384取模。醬紫每個key都會對應一個編号在 0~16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作。

雖然資料是分開存儲在不同節點上的,但是對用戶端來說,整個叢集Cluster,被看做一個整體。用戶端端連接配接任意一個node,看起來跟操作單執行個體的Redis一樣。當用戶端操作的key沒有被配置設定到正确的node節點時,Redis會傳回轉向指令,最後指向正确的node,這就有點像浏覽器頁面的302 重定向跳轉。

百度面試官:談談你對Redis高可用的了解

3.4 故障轉移

Redis叢集實作了高可用,當叢集内節點出現故障時,通過故障轉移,以保證叢集正常對外提供服務。

redis叢集通過ping/pong消息,實作故障發現。這個環境包括主觀下線和客觀下線。

主觀下線: 某個節點認為另一個節點不可用,即下線狀态,這個狀态并不是最終的故障判定,隻能代表一個節點的意見,可能存在誤判情況。

百度面試官:談談你對Redis高可用的了解

主觀下線

客觀下線: 名額記一個節點真正的下線,叢集内多個節點都認為該節點不可用,進而達成共識的結果。如果是持有槽的主節點故障,需要為該節點進行故障轉移。

  • 假如節點A标記節點B為主觀下線,一段時間後,節點A通過消息把節點B的狀态發到其它節點,當節點C接受到消息并解析出消息體時,如果發現節點B的pfail狀态時,會觸發客觀下線流程;
  • 當下線為主節點時,此時Redis Cluster叢集為統計持有槽的主節點投票,看投票數是否達到一半,當下線報告統計數大于一半時,被标記為客觀下線狀态。

流程如下:

百度面試官:談談你對Redis高可用的了解

客觀下線

故障恢複:故障發現後,如果下線節點的是主節點,則需要在它的從節點中選一個替換它,以保證叢集的高可用。流程如下:

百度面試官:談談你對Redis高可用的了解
  • 資格檢查:檢查從節點是否具備替換故障主節點的條件。
  • 準備選舉時間:資格檢查通過後,更新觸發故障選舉時間。
  • 發起選舉:到了故障選舉時間,進行選舉。
  • 選舉投票:隻有持有槽的主節點才有票,從節點收集到足夠的選票(大于一半),觸發替換主節點操作

繼續閱讀