本篇重點
主從庫同步原理、如何應對主從庫間網絡斷連風險
主從庫同步:全量複製、基於長連接的命令傳播、增量複製——應對主從庫間的網絡斷連
背景
若Redis只有一個實例運行,當該實例服務宕機後,宕機這段時間內Redis無法爲新來的數據請求提供服務。
Redis採取的解決方案是——增加副本冗餘量,即將一份數據同時保存在多個實例上——Redis主從庫模式
多實例保存同一份數據,需要考慮的問題:
- 副本間的數據如何保持一致?——數據同步
- 數據讀寫操作可以發給所有實例嗎?——數據讀寫
前言
Redis高可靠性的保證
- 數據儘量少丟失——持久化存儲(AOF/RDB)
- 服務儘量少中斷——主從庫(本質是增加副本冗餘量)
Redis主從庫模式
- Redis主從庫模式:保證數據副本一致性,且主從庫 “讀寫分離”
- 讀寫分離:
- 讀操作:主從庫都可接收
- 寫操作:主庫接收並執行,然後由主庫將寫操作同步給從庫
- 主從庫讀寫分離
- 主從庫同步的三個問題:
- 實現原理——如何完成主從庫
- 主庫數據是一次性傳給從庫,還是分批同步?
- 主從庫間網絡斷連了,數據還能保持一致嗎?
1. 主從庫第一次同步
replicaof
命令——建立主從庫關係[1]- 某Redis實例執行:
replicaof 主庫IP 主庫port
- 該實例與主庫建立主從庫關係,成爲主庫的從庫
- 某Redis實例執行:
- 主從庫第一次同步的三個階段(某實例第一次執行
replicaof
與主庫建立連接)- 建立連接,協商同步
- 從庫->主庫:psync
- 主庫->從庫:FULLRESYNC
- 從庫->主庫:psync
- 數據同步
- 主庫->從庫:RDB快照
- 從庫:清楚舊有數據,加載RDB
- 主庫在數據同步中,執行完RDB後,正常接收 & 執行的寫操作會被寫入 replication buffer 中
- 主庫->從庫:RDB快照
- 同步數據同步期間到來的寫操作——replication buffer
- 主庫->從庫:replication buffer
- 從庫:重新執行replication buffer中的操作
- 主庫->從庫:replication buffer
- 建立連接,協商同步
- Redis第一次主從同步流程
- 命令及參數解釋:
psync
命令形式:psync runID offset
runID:Redis實例ID,首次連接主庫ID未知,傳?
offset:複製進度,第一次複製傳-1
FULLRESYNC: 全量複製,第一次建立連接採用全量複製
Q:主從庫同步中,主庫執行RDB快照時,需要fork bgsave子進程,若從庫數量很多,則主庫fork的壓力就會增加,如何應對這種問題?緩解主庫壓力?
A:主從級聯模式——“主—從—從”,在從庫間建立“主從”關係,分擔主庫全量RDB的壓力
2. 主從級聯模式:分擔全量複製時的主庫壓力
- “主—從—從”模式:選擇一個從庫(內存配置較高),級聯其他從庫(replicaof)
3. 基於長連接的命令傳播——避免頻繁建立連接
- 主從庫建立網絡連接後,此時主從庫間已經完成了一次全量複製,後續Redis實例會維護這個網絡連接,在這個網絡連接上進行 “寫操作同步”
- “主—從—從”模式
4. 增量複製——主從網絡斷連後的解決方案
增量複製——僅同步斷連期間主庫收到的命令
repl_backlog_buffer——環形緩衝區,存儲主庫收到的寫命令——記錄主/從的寫/讀進度,通過offset
- master_repl_offset:主庫寫offset,遞增值
- slave_repl_offset: 從庫讀offset,遞增值
- 主從庫連接期間,master_repl_offset == slave_repl_offset
- 主從庫斷連後,master_repl_offset >= slave_repl_offset
Redis增量複製流程
- 斷連期間
- 主庫->replication buffer: 存儲寫命令(用於寫操作同步)
- 主庫->repl_backlog_buffer: 存儲寫命令
- 主庫:記錄 master_repl_offset
- 連接恢復時
- 從庫->主庫:
psync 主庫runID slave_repl_offset
- 主庫->從庫:slave_repl_offset之後的寫命令(全量/增量)
- 從庫:執行這些寫命令
- 從庫->主庫:
- Redis增量複製流程
Q: 主庫寫入速度快,從庫讀取慢,導致未讀的命令被新寫的命令覆蓋,引起的主從庫數據不一致問題如何預防?
A:
- 調整 repl_backlog_size 大小,通常 = 緩衝空間大小*2
- 或採用切片集羣分擔單個主庫的請求壓力(將寫請求分散到集羣中)
Q: 主從斷連時間過長,導致slave_repl_offset上的未讀數據已經被新寫入操作覆蓋(同上一個問題),如何數據同步?
A: 主庫採用全量複製
- 主庫判斷被覆蓋—— master_repl_offset - slave_repl_offset > repl_backlog_size (個人猜想)
- 若上式成立,則執行全量複製,否則增量複製
Q: 本篇討論的都是“主庫如何同步數據給從庫”、“斷連恢復後主庫如何同步斷連期間的數據給從庫”的問題,那麼當主庫掛了,Redis如何對外提供服務?此時從庫能起到什麼樣的作用?——主從庫模式的重要功能——服務儘量少中斷
A: Redis的哨兵機制[鏈接]
圖片來源於極客時間專欄《Redis核心技術與實戰》
Redis5.0前使用
slaveof
命令 ↩︎