談談 Redis 高可用之主從複製
之前總結過 redis 的持久化機制:深度剖析 Redis 持久化機制,持久化
機制主要解決 redis 數據單機備份問題;redis 的高可用需要考慮數據的多機備份,多機備份通過主從複製
來實現,這是 redis 高可用的基石。本文將詳細介紹 redis 主從複製的實現原理,在使用過程中應該注意的問題和相關配置。
1. CAP 理論
CAP 理論是分佈式領域的牛頓定律,所有的分佈式存儲中間件都要使用它作爲理論基石。如下圖所示:
這個原理很簡單,首先明確幾個概念:
-
C : Consistent, 一致性
-
A : Availability, 可用性
-
P : Partition tolerance, 分區容忍性
分佈式系統的節點往往分佈在不同的機器上,它們之間由網絡進行隔離,當網絡斷開時就會產生網絡分區
。網絡分區不可避免,但是當網絡分區發生時,對分佈式系統中一個節點的修改操作無法同步給其它節點,數據一致性
也就無法滿足;想要滿足一致性
,除非犧牲可用性
,也就是暫停分佈式節點服務,等到網絡恢復,數據一致後,再對外提供服務。分佈式系統中網絡分區
不可避免,一致性
和可用性
水火不容。這就是 cap 理論: 網絡分區發生時,一致性和可用性兩難全。
2.redis 主從複製
2.1 概述
互聯網圈經常談 “三高” 架構:高併發、高性能、高可用。對於 redis 來說,高併發、高性能可以保證,高可用需要架構的設計,單機 redis 由於存在系統崩潰、硬盤故障的風險,還有內存的限制,所以企業一般都會搭建主從,對系統有更高要求會搭建集羣。
爲了保持數據一致性,主節點 (master) 只寫,從節點 (slave) 只讀,數據由主節點複製給從節點,這個複製的過程就是主從複製。
redis 的主從複製是異步的,分佈式的 redis 系統並不滿足一致性要求,但是在網絡斷開的情況下,主節點依然可以對外提供服務,滿足可用性。redis 保證最終一致性,從節點會努力追趕主節點,最終從節點的狀態會和從節點保持一致。網絡斷開的情況下,主從節點數據會出現大量的不一致,但一旦網絡恢復,從節點會繼續追趕主節點,最終達到和主節點狀態一致。
爲了減輕 redis 主節點的同步負擔,redis 的後續版本還增加了從從同步,與此同時,數據一致性會變差。
2.2 主從複製的作用
主從複製在服務中起到了什麼效果呢?
-
讀寫分離:master 寫,slave 讀,提高服務器的讀寫負載能力。
-
負載均衡:基於主從架構,配合讀寫分離,由 slave 分擔 master 負載,並根據需求的變化,改變 slave 的數量,通過多個從節點分擔數據讀取負載,大大提高 redis 服務器併發量和數據吞吐量。
-
故障恢復:當 master 出現問題時,由 slave 提供服務,實現快速的故障恢復。
-
數據冗餘:實現數據熱備份,是持久化之外的一種數據冗餘方式。
-
高可用基石:基於主從複製,構建哨兵模式與集羣,實現 redis 的高可用方案。
2.3 怎樣配置實現主從複製?
有三種配置實現 redis 的主從:
- 方式一:客戶端發送命令
slaveof <masterip> <masterport>
- 方式二:啓動服務器時添加參數
redis-server -slaveof <masterip> <masterport>
- 方式三:服務器配置文件中配置 (通過 redis.conf)
slaveof <masterip> <masterport>
- 主從斷開連接,可以從客戶端發送命令
slaveof no one
- 服務端設置了授權訪問
2.4 redis 主從複製的工作流程
redis 主從複製實現過程有三個階段:
建立連接、數據同步、命令傳播。建立連接階段主從節點建立通信的橋樑,彼此之間同步一些基礎信息;數據同步階段實現從節點全量同步主節點的數據;從節點同步完主節點數據之後,就進入了命令傳播階段,主節點接收寫請求,數據不斷髮生變化,通過命令傳播階段主節點將數據源源不斷的同步給從節點。下邊我們詳細介紹主從複製這三個階段的工作細節和注意事項。
2.4.1 建立連接階段
建立 slave 到 master 的連接,使 master 能識別 slave, 並保存 slave 的端口號;與此同時,slave 也保存 master 的地址和端口號信息。
-
slave 發送
slaveof ip port
命令給 master,master 響應 slave -
slave 保存 master 的 ip 和端口號,建立 socket 連接
-
在 socket 連接之上,主從節點實現了心跳機制,這部分內容也比較重要,後邊會提到。
-
如果有認證機制,從節點通過上邊說到的認證指令,發送認證信息給 master, 實現認證。
-
從節點將自己的端口信息發送發送給主節點,主節點保存。
通過以上過程主從之間的連接就建立了。
2.4.2 數據同步階段
數據同步階段實現的功能是從節點從主節點同步全量的數據。這個過程又分爲幾個小階段,最主要的就是數據的全量複製
和部分複製
, 對應的流程就是主節點發送 rdb 文件同步數據和發送緩衝區寫命令 (aof) 同步數據給從節點。下圖是實現細節:
-
首先 slave 節點先發起命令
psync ? -1
, 向 master 節點要全量數據。 -
master 節點接收到指令以後,執行 bgsave,將當前內存數據快照保存爲 rdb 文件,這個過程爲了不影響主節點繼續對外提供服務,採用了
Copy On Write
技術。與此同時,master 節點也會將 bgsave 保存快照期間接收到的寫更新命令添加到複製擠壓緩衝區
當中。master 節點 rdb 文件生成完畢以後,會通過第一階段建立的 socket 連接將它發送給 slave 節點,還會發送+FULLRESYNC runid offset
給 slave 節點,告訴 slave 節點自己的runid
和offset
。
什麼是
runid
?
redis-server
在每次啓動的時候都會生成一個 runid, 因爲 redis-server 是一個守護進程,所以在運行期間,runid 不會發生變化,可以通過info server
指令查看 runid,它是一個 40 位字符長度的字符串。上文提到的psync
有兩個參數,和+FULLRESYNC
一樣:psync <runid> <offset>
;runid 的意義是什麼呢?當 master 節點發生故障發生了變更後,在接到 slave 的指令以後,對比參數中的 runid 如果和自己的 runid 不一致,就會再次進行全量複製,因爲換主了。
什麼是
複製擠壓緩衝區
和offset
?複製擠壓緩衝區是一個先進先出 (FIFO) 的環形隊列,用於存儲服務端執行過的命令,每次傳播命令,master 節點都會將傳播的命令記錄下來,保存在這裏。
複製擠壓緩衝區由兩部分組成:偏移量和字節值。字節值是 redis 指令字節的存儲(redis 指令以一種 Redis 序列化文本協議的格式存儲),偏移量 offset 就是當前字節值在環形隊列中的偏移量。
-
slave 節點接收完 master 節點同步的 rdb 文件之後,將 rdb 的內容加載到自己的內存,然後將 master 節點的
runid
和offset
記錄下來。 -
有了 master 節點的
runid
和offset
,在加載完 rdb 文件之後,就開始向 master 節點發送新的命令psync runid offset
,向 master 節點要新數據。新數據是 master 節點在 bgsave 生成 rdb 文件時和向 slave 同步數據的這段時間產生的,所以這段時間的工作也稱爲部分複製
。 -
master 節點收到 slave 節點發送的請求數據命令之後,會檢查 runid 是否一致(是否換主),offset 是否一致 (因爲複製擠壓緩衝區是定長的,所有有可能會溢出),這兩個條件只要有一個不滿足,master 就會向 slave 再次全量的同步數據 (讀者可能會發現,如果 master 節點寫併發很高,複製擠壓緩衝區又設置的比較小的話,可能會每次向 slave 同步完數據以後,每次複製擠壓緩衝區都會溢出,造成主從之間循環的全量複製。這確實是應該規避的問題!我們後邊會針對主從複製應該考慮的問題做一個總結)。在 runid 和 offset 都滿足的情況下,master 節點就會向 slave 節點發送指令
+CONTINUE offset
,接着從 offset 位置開始同步數據,數據都在主節點的複製擠壓緩衝區中了,所以直接複製發送就可以了。 -
slave 節點接收到 master 節點發送的
+CONTINUE offset
指令之後,更新自己保存的 offset 值,然後將從 master 節點同步過來的數據,使用 bgrewriteaof,重放 aof 數據。
到這裏,主從複製的第二階段:數據同步階段工作就完成了。
2.4.3 命令傳播階段
命令傳播階段類似於數據同步階段的部分複製
,當 master 節點數據被修改以後,就和 slave 節點的數據不一致了,這個時候 master 節點就會根據 slave 上報的 offset 開始傳播數據(一主多從的架構中,master 節點要記錄每一個 slave 的 offset)。slave 接收到數據以後,執行 bgrewriteaof 重放數據。在這個工作過程中,如果因爲網路問題導致 offset 溢出或者換主的情況,主從之間還是會進行數據的全量同步的。
2.5 心跳機制
進入命令傳播階段以後,master 節點與 slave 節點需要進行信息傳遞,使用心跳機制進行維護,實現雙方保持在線。
master 節點心跳使用指令PING
, 由配置repl-ping-slave-period
決定,默認 10 秒, 作用是判斷 slave 是否在線,可以通過info replication
獲取 slave 最後一次連接到現在的時間間隔,lag 的值維護在 0 和 1 視爲正常。
slave 節點的心跳任務使用指令REPLCONF ACK {offset}
,週期是 1 秒,slave 的心跳任務有兩個作用:
-
彙報自己的 offset 給 master, 這在數據傳播起到了關鍵性作用,因爲 master 節點向 slave 節點傳播數據,offset 是一項非常重要的指標。
-
判斷 master 是否在線
在心跳階段應該注意:當 slave 節點多數掉線,或者延遲過高時,master 節點爲了保證數據的穩定性,將拒絕所有信息的同步。有如下配置:
min-slaves-to-write 2
min-slaves-max-lag 8
上述配置含義是:當 slave 數量小於 2 個,或者所有的 slave 的延遲都大於等於 8 秒時,強制關閉 master 寫功能,停止數據同步。
3. 主從複製常見問題
上邊介紹的主從複製是建立在主從節點間的網絡和服務都正常的情況下,業務場景中要考慮更多的實際情況。
3.1 master 重啓
伴隨着系統的運行,master 節點的內存數據量變得很大的情況下,一旦 master 節點重啓,runid 將發生變化,會導致 slave 的全量複製操作。
這裏有一個優化方案:在 master 節點內部創建 master_replid 變量,使用 runid 相同的策略生成,長度 41 位,發送給所有的 slave 節點。在 master 節點關閉時,執行命令 shutdown save,進行 RDB 的數據持久化,將 runid 與 offset 保存在 RDB 文件中。在 RDB 文件中有了 repl-id 和 repl-offset 信息以後,通過指令redis-check-rdb
命令可以查看這些信息。在 master 節點重啓後,將 RDB 文件加載到內存中以後,也會將repl-id
和repl-offset
加載到內存中。通過 info 指令可以查看:
master_repl_id = repl
master_repl_offset = repl-offset
作用是:master 節點重啓之後會保存原來的 runid, 重啓後恢復該值,會讓所有的 slave 節點認爲還是之前的 master 節點。
3.2 複製積壓緩衝區太小
當複製積壓緩衝區太小的時候,當 master 節點寫併發很大,master 節點和 slave 節點網絡有抖動的時候,就會導致數據同步不及時,造成 offset 溢出,進而導致全量複製
。這個時候,我們可以考慮修改複製積壓緩衝區的大小,由配置repl-backlog-size
控制。設置多大比較合適呢,這要根據 master 的併發量和網絡情況做具體的評估。
3.3 slave 執行了 keys * 、hgetall 等命令
前邊內容我們提到 slave 節點每秒都會發送 REPLCONF ACK 指令到 master 節點,master 節點調用複製函數 relicationCron()同步數據給 slave 節點時,如果 slave 節點執行了 keys *、hgetall 等阻塞命令的時候,就會在很長一段時候得不到響應。這就會導致 master 的各種資源 (輸出緩衝區、帶寬、連接) 等被佔用。master 節點的 CPU 就會變高,slave 頻繁的斷開連接。
解決方案是 master 節點通過配置:repl-timeout
設置合理的超時時間 (默認 60s),超過改值,master 節點將釋放 slave 節點。
3.4 master 節點發送 ping 指令頻度低,網絡存在丟包
master 節點默認 10s 向 slave 節點發送一次 ping 指令,因爲 master 節點不僅要處理大量的寫任務,還可能維護着多個 master,所以 ping 設置的不太及時。但是當 ping 指令在網絡中存在丟包時,master 節點如果設置的超時時間太短,就會導致 master 節點與 slave 節點斷開連接。
解決方案有:提高 master 節點 ping 的頻度,超時時間 repl-time 設置爲 ping 指令時間的 5~10 倍。
3.5 網絡信息不同步,數據發送有延遲
當主從同步中網絡數據發送有延遲的時候,就會造成多個 slave 獲取到的數據不同步,解決方案是優化 master 節點和 slave 節點的網絡環境,通常是放置在一個機房部署。另外要監控 master 和 slave 節點的延遲,如果延遲過大,可以暫時屏蔽對 slave 節點的訪問。通過下面指令設置:
slave-serve-stale-data yes | no
開啓後,slave 節點僅僅能響應 info、slaveof 等少數命令,除非對數據一致性要求很高,否則不要輕易這樣使用。
- 總結
本文主要總結了 redis 實現主從複製的實現細節和注意事項。redis 的主從複製是實現高可用的重要基石,後邊的文章將總結哨兵和集羣的搭建。
如喜歡本文,請點擊右上角,把文章分享到朋友圈
如有想了解學習的技術點,請留言給若飛安排分享
作者:繪你一世傾城
來源:juejin.cn/post/6844904098773352455
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/0lROAM5nA6B3ptQ7OvWscg