Redis
主從復制是指:將一台 Redis
服務器的數據復制到其它的 Redis 服務器,前者所在的 Redis
服務器也被稱為 “主節點”(Master / Leader),后者則被稱為 “從節點”(Slave / Follower)。數據從主節點復制到從節點,主節點的主要任務是實現寫入數據的任務(也有讀數據的權限),而從節點則只負責讀取數據。在 Redis
的默認配置中,每個啟動的 Redis
服務都是主節點
主節點
一個主節點可以有多個從節點,但是對於每個從節點來講,它都只能屬於一個主節點。即主節點和從節點之間的對應關系為 \(1\) 對 \(N\) ,如下圖所示:

主從復制的目的
由於單個的 Redis
服務可能會出現異常,使得 Redis
服務不可用,因此有必要對這種情況進行進一步的處理。
主從復制的目的就是為了解決單一的 Redis
可能會出現的問題,同時,通過主從復制實現 Redis
的讀寫分離,能夠進一步提高整體 Redis
的性能。
主從復制只要提供了如下的功能:
- 數據冗余:主從復制實現了數據的備份,實際上提供了數據冗余的實現方式
- 故障恢復:當主節點出現異常時,可以將一個從節點選舉成為一個新的主節點,從而提供了故障恢復的功能
- 負載均衡:在主從復制的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務,分擔服務器的負載。在寫少讀多的業務場景下,通過多個從節點分擔讀負載,可以極大地提高
Redis
服務能夠承載的並發量 - 高可用
環境搭建
具體可以參考官方文檔:https://redis.io/topics/cluster-tutorial
或者 https://segmentfault.com/a/1190000038995016 也是一篇介紹比較全面的博客
集群搭建的步驟比較簡單,在這里略過
主從復制的實現原理
Redis
的主從復制分為以下兩個階段:sync
階段和 command propagate
階段。
sync
(同步)階段
當從節點啟動之后,會發送 sync
指令給主節點,要求全量同步數據,具體的步驟如下圖所示:

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

復制積壓緩沖區:由主節點維護的的一個固定長度(默認為 \(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
節點運行狀態 - 通知:當被監控的
Redis
節點出現問題時,Sentinel
可以通過向API
或者管理員以及其它應用發送通知 - 自動故障轉移:當一個主服務器不能正常工作時,
Sentinel
會開始一次自動故障遷移,它會在失效的Redis
集群中尋找一個有效的節點,並將它升級為新的Master
節點,並將原來失效的Master
節點降級為Slave
節點。當客戶端試圖訪問已經失效的Master
節點時,Redis
集群也會想客戶端返回新的Master
節點的地址,使得Redis
集群可以使用新的Master
節點代替失效的Master
節點
由於使用單個的 “哨兵” 來監視 Redis
集群的節點也不是完全可靠的,因為 “哨兵” 節點也有可能會出現故障,因此,一般情況下會使用多個 “哨兵” 節點來監視整個 Redis
集群,如下圖所示:

由於存在多個 哨兵
節點,因此在 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
命令讓其他節點成為該節點的從節點
參考: