Redis分佈式集羣方案

1 爲什麼要搭建Redis集羣

我們知道單線程的Redis性能已經很高了,但實際應用中我們還是要搭建Redis集羣,原因主要有三點:

  • 可用性和安全性方面,單機的Redis服務一旦宕機,會造成服務不可用,嚴重的會丟失數據,造成不可估量的損失。
  • 存儲數據有限,Redis是內存數據庫,如果數據量大容易受到硬件條件限制。
  • 吞吐量性能考慮,單機的Redis性能雖高,但如果在高併發場景下,性能還是會受到影響。

2 Redis主從複製

2.1 主從複製配置

在每個 slave 節點的 redis.conf 配置文件增加一行
slaveof 192.168.8.203 6379
在主從切換的時候,這個配置會被重寫成:
replicaof 192.168.8.203 6379
或者在啓動服務時通過參數指定 master 節點:
./redis-server --slaveof 192.168.8.203 6379
或在客戶端直接執行 slaveof xx xx,使該 Redis 實例成爲從節點。
啓動後,查看集羣狀態:

redis> info replication

從節點不能寫入數據(只讀),只能從 master 節點同步數據。
主節點寫入後,slave 會自動從 master 同步數據。

2.2 主從複製原理

2.2.1 連接階段
  1. slave node 啓動時(執行 slaveof 命令),會在自己本地保存 master node 的 信息,包括 master node 的 host 和 ip。
  2. slave node 內部有個定時任務 replicationCron(源碼 replication.c),每隔 1 秒鐘檢查是否有新的 master node 要連接和複製,如果發現,就跟 master node 建立 socket 網絡連接,如果連接成功,從節點爲該 socket 建立一個專門處理複製工作的文件 事件處理器,負責後續的複製工作,如接收 RDB 文件、接收命令傳播等。 當從節點變成了主節點的一個客戶端之後,會給主節點發送 ping 請求。
2.2.2 數據同步階段
  1. master node 第一次執行全量複製,通過 bgsave 命令在本地生成一份 RDB 快 照,將 RDB 快照文件發給 slave node(如果超時會重連,可以調大 repl-timeout 的值)。 slave node 首先清除自己的舊數據,然後用 RDB 文件加載數據。

問題: 生成 RDB 期間,master 接收到的命令怎麼處理?
開始生成 RDB 文件時,master 會把所有新的寫命令緩存在內存中。在 slave node。保存了 RDB 之後,再將新的寫命令複製給 slave node。

2.2.3 命令傳播階段
  1. master node 持續將寫命令,異步複製給 slave node
    延遲是不可避免的,只能通過優化網絡。

repl-disable-tcp-nodelay no
當設置爲 yes 時,TCP 會對包進行合併從而減少帶寬,但是發送的頻率會降低,從 節點數據延遲增加,一致性變差;具體發送頻率與 Linux 內核的配置有關,默認配置爲 40ms。當設置爲 no 時,TCP 會立馬將主節點的數據發送給從節點,帶寬增加但延遲變 小。

一般來說,只有當應用對 Redis 數據不一致的容忍度較高,且主從節點之間網絡狀 況不好時,纔會設置爲 yes;多數情況使用默認值 no。
問題: 如果從節點有一段時間斷開了與主節點的連接是不是要重新全量複製一遍? 如果可以增量複製,怎麼知道上次複製到哪裏?
通過 master_repl_offset 記錄的偏移量

2.3 主從複製的不足

主從模式解決了數據備份和性能(通過讀寫分離)的問題,但是還是存在一些不足:

  • RDB 文件過大的情況下,同步非常耗時。
  • 在一主一從或者一主多從的情況下,如果主服務器掛了,對外提供的服務就不可 用了,單點問題沒有得到解決。如果每次都是手動把之前的從服務器切換成主服務器, 這個比較費時費力,還會造成一定時間的服務不可用。

3 可用性保證之 哨兵機制Sentinel

3.1 Sentinel 原理

   如何實現主從的自動切換?我們的思路: 創建一臺監控服務器來監控所有 Redis 服務節點的狀態,比如,master 節點超過一 定時間沒有給監控服務器發送心跳報文,就把 master 標記爲下線,然後把某一個 slave 變成 master。應用每一次都是從這個監控服務器拿到 master 的地址。

問題: 如果監控服務器本身出問題了怎麼辦?那我們就拿不到 master 的地址了, 應用也沒有辦法訪問。 那我們再創建一個監控服務器,來監控監控服務器……似乎陷入死循環了,這個問題 怎麼解決?這個問題先放着。 Redis 的 Sentinel 就是這種思路:通過運行監控服務器來保證服務的可用性。
從 Redis2.8 版本起,提供了一個穩定版本的 Sentinel(哨兵),用來解決高可用的 問題。它是一個特殊狀態的 redis 實例。
我們會啓動一個或者多個 Sentinel 的服務(通過 src/redis-sentinel),它本質上只 是一個運行在特殊模式之下的 Redis,Sentinel 通過 info 命令得到被監聽 Redis 機器的 master,slave 等信息。
爲了保證監控服務器的可用性,我們會對 Sentinel 做集羣的部署。Sentinel 既監控 所有的 Redis 服務,Sentinel 之間也相互監控。 注意:Sentinel 本身沒有主從之分,只有 Redis 服務節點有主從之分。 概念梳理:master,slave(redis group),sentinel,sentinel 集合。

3.1.1 服務下線

Sentinel 默認以每秒鐘 1 次的頻率向 Redis 服務節點發送 PING 命令。如果在 down-after-milliseconds 內都沒有收到有效回覆,Sentinel 會將該服務器標記爲下線主觀下線

#sentinel.conf
sentinel down-after-milliseconds < master-name > < milliseconds >

這個時候 Sentinel 節點會繼續詢問其他的 Sentinel 節點,確認這個節點是否下線, 如果多數 Sentinel 節點都認爲 master 下線,master 才真正確認被下線客觀下線, 這個時候就需要重新選舉 master。

3.1.2 故障轉移

如果 master 被標記爲下線,就會開始故障轉移流程。 既然有這麼多的 Sentinel 節點,由誰來做故障轉移的事情呢? 故障轉移流程的第一步就是在 Sentinel 集羣選擇一個 Leader,由 Leader 完成故障 轉移流程。Sentinle 通過 Raft 算法,實現 Sentinel 選舉。

問題: 怎麼讓一個原來的 slave 節點成爲主節點?
1、選出 Sentinel Leader 之後,由 Sentinel Leader 向某個節點發送 slaveof no one 命令,讓它成爲獨立節點。
2、然後向其他節點發送 slaveof x.x.x.x xxxx(本機服務),讓它們成爲這個節點的 子節點,故障轉移完成。
問題: 這麼多從節點,選誰成爲主節點?
關於從節點選舉,一共有四個因素影響選舉的結果,分別是斷開連接時長、優先級 排序、複製數量、進程 id。
如果與哨兵連接斷開的比較久,超過了某個閾值,就直接失去了選舉權。如果擁有 選舉權,那就看誰的優先級高,這個在配置文件裏可以設置(replica-priority 100), 數值越小優先級越高。
如果優先級相同,就看誰從 master 中複製的數據最多(複製偏移量最大),選最多 的那個,如果複製數量也相同,就選擇進程 id 最小的那個。

3.2 Sentinel 的功能總結

監控:Sentinel 會不斷檢查主服務器和從服務器是否正常運行。
通知:如果某一個被監控的實例出現問題,Sentinel 可以通過 API 發出通知。
自動故障轉移(failover):如果主服務器發生故障,Sentinel 可以啓動故障轉移過 程。把某臺服務器升級爲主服務器,併發出通知。
配置管理:客戶端連接到 Sentinel,獲取當前的 Redis 主服務器的地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章