Redis 的主從復制


Redis 主從復制是指:將一台 Redis 服務器的數據復制到其它的 Redis 服務器,前者所在的 Redis 服務器也被稱為 “主節點”(Master / Leader),后者則被稱為 “從節點”(Slave / Follower)。數據從主節點復制到從節點,主節點的主要任務是實現寫入數據的任務(也有讀數據的權限),而從節點則只負責讀取數據。在 Redis 的默認配置中,每個啟動的 Redis 服務都是主節點

主節點

一個主節點可以有多個從節點,但是對於每個從節點來講,它都只能屬於一個主節點。即主節點和從節點之間的對應關系為 \(1\)\(N\) ,如下圖所示:

master.png

主從復制的目的

由於單個的 Redis 服務可能會出現異常,使得 Redis 服務不可用,因此有必要對這種情況進行進一步的處理。

主從復制的目的就是為了解決單一的 Redis 可能會出現的問題,同時,通過主從復制實現 Redis 的讀寫分離,能夠進一步提高整體 Redis 的性能。

主從復制只要提供了如下的功能:

  • 數據冗余:主從復制實現了數據的備份,實際上提供了數據冗余的實現方式
  • 故障恢復:當主節點出現異常時,可以將一個從節點選舉成為一個新的主節點,從而提供了故障恢復的功能
  • 負載均衡:在主從復制的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務,分擔服務器的負載。在寫少讀多的業務場景下,通過多個從節點分擔讀負載,可以極大地提高 Redis 服務能夠承載的並發量
  • 高可用

環境搭建

具體可以參考官方文檔:https://redis.io/topics/cluster-tutorial

或者 https://segmentfault.com/a/1190000038995016 也是一篇介紹比較全面的博客

集群搭建的步驟比較簡單,在這里略過

主從復制的實現原理

Redis 的主從復制分為以下兩個階段:sync 階段和 command propagate 階段。

sync(同步)階段

當從節點啟動之后,會發送 sync 指令給主節點,要求全量同步數據,具體的步驟如下圖所示:

redis-sync.png
  1. Slave 節點向 Master 節點發送 sync 指令,以請求數據同步
  2. Master 節點在接收到 sync 指令后,會執行一次 BGSAVE 指令,將當前 Master 節點中的數據保存到對應的 RDB 文件中。當 Master 節點完成 RDB 文件的導出后,再將導出的 RDB 文件發送給 Slave 節點。由於在這個過程中 Master 節點依舊有可能發生數據寫入操作,在這種情況下 Master 節點會將執行的指令放入到對應的緩沖區
  3. Slave 節點在接受到 Master 節點導出的 RDB 文件之后,會刪除現有節點的所有數據,然后加載這個 RDB 文件的內容到 Slave 節點
  4. Slave 節點數據加載完成之后,Master 會將緩沖區中暫存的指令發送到 Slave 節點
  5. Slave 執行收到的指令,完成數據的同步

Command Propagate 階段

這個階段也被稱為 “命令傳播” 階段,數據同步完成之后,如果后續 Master 節點繼續收到了新的寫入操作的指令,那么也需要將該命令傳播到 Slave 節點以完成數據的同步。這個過程就被稱為 “命令傳播”

psync 指令

上文提到 Slave 節點在啟動時通過發送 sync 指令到 Master 節點用戶獲取數據的同步,在 Slave 節點啟動的時候執行 sync 指令來進行同步是很合理的。但是如果在運行的過程中,由於網絡抖動,使得 Slave 斷開了同 Master 節點的連接,那么 Slave 節點再再次連接到 Master 節點時依舊通過 sync 指令來進行同步,那么性能就會非常差。

為了解決這個問題,自 Redis 2.8 開始,引入了 psync 指令來代替 sync 指令

psync 指令會根據不同的情況,來執行全量重同步和部分重同步

  • 全量重同步:當從節點是第一次與主節點建立連接的時候,那么就會執行全量重同步,這個同步過程和上文 sync + command propagate 進行同步的過程一致
  • 部分重同步:從 Slave 節點的復制偏移量在 Master 節點的復制積壓區中尋找待同步的數據

psync 通過 Redis 的節點 ID 來判斷 Slave 節點是否是第一次與 Master 節點進行同步

復制偏移量:Master 節點和 Slave 節點都保存着一份賦值偏移量,當 Master 節點每次向 Slave 節點發送 \(n\) 字節的數據時,就會在 Master 節點上將偏移量增加 \(n\);而每次 Slave 節點在接收到 \(n\) 字節的數據時,也會將節點上的偏移量增加 \(n\)

在 “命令傳播階段”,Slave 節點會定期發送心跳 REPLYCONF ACK {offset} 指令,這里的 offset 就是 Slave 節點的偏移量。當 Master 節點在收到這個心跳指令之后,會對比自己的 offset 和收到的 offset,如果發現有數據丟失,那么 Master 節點就會推送丟失的那段數據給 Slave 節點。這個過程如下圖所示:

redis-psync.png

復制積壓緩沖區:由主節點維護的的一個固定長度(默認為 \(1\) MB)的隊列,該隊列存儲了每個字節值與對應的復制偏移量。

由於復制積壓緩沖區的大小是固定的,因此它保存的是最近 Master 節點執行的寫操作命令。當 Slave 節點將 offset 發送給 Master 節點之后,Master 節點會根據 offset 與復制積壓緩沖區的大小來決定是否可以使用部分重同步。如果 offset 后面的數據依舊存在於復制積壓緩沖區,則執行部分重同步,否則執行全量重同步


節點 ID:Redis 實例啟動之后,就會產生一個唯一的標識 ID,用於標識當前的 Redis 實例

Master 節點同 Slave 節點進行第一次連接同步時,Master 節點會將 ID 發送給 Slave 節點,Slave 節點在收到 Master 節點的 ID 之后會將他們進行保存。如果在 Slave 節點和 Master 節點之間的連接由於某些原因斷開了,那么當 Slave 在恢復到與 Master 節點之間的鏈接時,Slave 節點會將這個 ID 發送給 Master 節點,Master 節點會將該 ID 與自己的實例 ID 進行比較,如果相同,則說明該 Slave 之前同 Master 節點是連接的。

Redis 哨兵

上面的集群搭建之后,如果 Master 節點崩潰了,在上面的情況下不會將 Slave 節點轉換為 Master 節點,因此 Master 節點崩潰之后整個 Redis 集群就不能再執行寫入操作。

為了解決這個問題,提高系統的可用性,Redis 提供了 Sentinel(哨兵)來實現 Slave 節點到 Master 節點的轉換

“哨兵” 節點本質上也是一個 Redis 節點,但是和 Master 節點和 Slave 節點不同,“哨兵” 節點只是監視 Master 節點和 Slave 節點,並不執行相關的業務操作。具體關系如下圖所示:

redis-sentinel.png

“哨兵” 的主要有以下幾個作用:

  • 監控 Redis 節點運行狀態
  • 通知:當被監控的 Redis 節點出現問題時,Sentinel 可以通過向 API 或者管理員以及其它應用發送通知
  • 自動故障轉移:當一個主服務器不能正常工作時,Sentinel 會開始一次自動故障遷移,它會在失效的 Redis 集群中尋找一個有效的節點,並將它升級為新的 Master 節點,並將原來失效的 Master 節點降級為 Slave 節點。當客戶端試圖訪問已經失效的 Master 節點時,Redis 集群也會想客戶端返回新的 Master 節點的地址,使得 Redis 集群可以使用新的 Master 節點代替失效的 Master 節點

由於使用單個的 “哨兵” 來監視 Redis 集群的節點也不是完全可靠的,因為 “哨兵” 節點也有可能會出現故障,因此,一般情況下會使用多個 “哨兵” 節點來監視整個 Redis 集群,如下圖所示:

redis-sentinel.png

由於存在多個 哨兵 節點,因此在 Redis Sentinel 中,對於 Redis 節點的下線也有區分:

  • 主觀下線(Subjectively Down,即 SDOWN):指單個 Sentinel 節點對集群中的節點作出下線判斷
  • 客觀下線(Objectively Down,即 ODOWN):指多個 Sentinel 節點對集群中的節點作出 “SDOWN” 判斷,並且通過 SENTINEL is-master-down-by-addr 命令互相交流之后,作出 Redis 節點下線的判斷

一個 Sentinel 節點可以通過向另一個 Sentinel 節點發送 SENTINEL is-master-down-by-addr 命令來詢問對方是否認為給定的節點已經下線

Redis Sentinel 部署

具體可以參考:https://www.cnblogs.com/youzhibing/p/8466491.html#autoid-4-0-0,這個部署比較簡單,在此略過

值得注意的是有關 sentinel 的配置文件:


# sentinel的端口號,如果配置3個Sentinel,只需要修改這個port即可,即:26380、26381、26382
port 26381

# 設置當前 sentinel 是否放入后台運行
daemonize yes

# 設置當前 sentinel 的日志輸出文件
logfile "/tmp/sentinel/sentinel-26381.log"

# 該 sentinel 對應的進程對應的文件描述符
pidfile "/var/run/redis-sentinel-26381.pid"

# 監視127.0.0.1:6701、6700、6702 的節點,且至少有2個Sentinel判斷主節點失效,才可以自動故障遷移
sentinel monitor myslave-2 127.0.0.1 6702 2
sentinel monitor mymaster 127.0.0.1 6700 2
sentinel monitor myslave-1 127.0.0.1 6701 2

# 那么Sentinel將這個服務器標記為主觀下線(subjectively down,簡稱 SDOWN )
sentinel down-after-milliseconds mymaster 60000

具體的配置文件的相關屬性可以參考官方文檔:https://redis.io/topics/sentinel

實現原理

對於節點的檢測,主要通過以下三種方式來進行檢測:

  • 每個 Sentinel 會每隔 \(10\)s 向主節點中發送 INFO 指令,通過該指令可以獲得整個 Redis 節點的拓撲圖。在這個時候,如果有新的節點加入或者有節點退出當前的集群,那么 Sentinel 就能夠感知到拓撲圖結構的變化。
  • 每個 Sentinel 節點每隔 \(2\)s 會向指定的 Channel 發布自己對 Master 節點是否正常的判斷以及當前 Sentinel 節點的信息,通過訂閱這個 Channel,可以獲得其它 Sentinel 節點的信息以及對 Master 節點的存活狀態的判斷
  • 每個 Sentinel 節點每隔 \(1\)s 就會向所有節點(包括 Sentinel 節點、Master 節點以及 Slave 節點)發送 PING 指令來檢測節點的存活狀態

主節點的選舉流程:

  • 當一個 Sentinel 節點判斷 Master 節點不可用時,首先進行 “SDOWN”(主觀下線),此時,這個 Sentinel 通過 SENTINEL is-masterdown-by-addr 指令獲取其它哨兵節點對於當前 Master 節點的判斷情況,如果當前哨兵節點對於當前 Master 節點的下線判斷數量超過了在配置文件中定義的票數,那么該 Master 節點就被判定為 “ODOWN”(主觀下線)
  • Sentinel 節點列表中也會存在一個 Leader Sentinel,該 Sentinel 會從原主節點的從節點中選出一個新的主節點,具體步驟如下所示:
    • 首先,過濾掉所有的 ODOWN 節點
    • 選擇 slave-priority 最大的節點,如果存在則選擇這個節點為新的主節點,如果沒有則繼續下面的流程
    • 選出復制偏移量最大的節點,如果有則返回;如果沒有則繼續執行下面的流程
    • 選擇 run_id (服務運行 id) 最小的節點
  • 當選擇出新的主節點之后,Leader Sentinel 節點會通過 SLAVEOF NO ONE 命令讓選擇出來的節點成為主節點,然后通過 SLAVEOF 命令讓其他節點成為該節點的從節點

參考:

[1] https://mp.weixin.qq.com/s/bs5NfSkQlFbFp7KMQ9aLmw

[2] https://redis.io/topics/sentinel


免責聲明!

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



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