在redis恢復數據時我們可以依賴於aof日誌或rdb日誌,但是redis在運行中該如何保證服務的可靠性,就需要依賴redis主從和哨兵集群。
這一篇主要學習下如何通過redis的主從設計來保證服務的高可用。主從模式的思想就在於增加從副本,將一份數據同時保存在主從多個實例上,這樣即使有一個實例出現了故障,其他實例也可以繼續對外提供服務,不會影響業務的使用。
一、redis主從模式的讀寫分離
redis通過多實例來保存數據,為了保證redis實例數據的一致性,因此在主從模式下,主從之間採用的是讀寫分離的方式。
讀操作:主、從實例都可以接收命令操作讀請求。
寫操作:只有主可以執行,主執行完畢後將寫操作同步給從庫。
以上的方式來解決多實例的一致性,也是比較方便簡單的。因為如果主從實例都可以接受寫請求,並且假設對同一個key修改了多次,並且每次都是請求到不同的主從實例上,那麼最直觀的結果可能出現數據不一致的情況。
如果需要強制保持數據一致,那麼肯定需要涉及到鎖的開銷,阻塞住其他讀寫請求。
因此有了主從模式的讀寫分離,寫命令就只會在主實例中修改,客戶端就不用關心從實例,這條寫命令會由主實例異步同步給從庫。
二、主從模式的第一次數據同步
主從之間通過slaveof命令來將執行該命令的實例變為從庫。
比如,有實例1(172.168.19.1)和實例2(172.168.19.2),在實例2中執行命令 - slaveof 172.168.19.1 6379。
接下來,實例2就變成了實例1的從庫,然後會從實例1中複製數據。
· 第一次同步數據時的步驟
(1)主從建立連接,準備進行全量複製。從庫發送psync請求告訴主庫,要開始同步數據,主庫確認回復後,主從庫開始同步數據。
(2)主庫將所有數據同步給從庫,從庫收到數據後在本地完成數據加載。在加載主庫的數據主要是通過主庫的rdb快照文件來直接加載數據,並且在加載前會先清空當前從庫中的數據。
(3)主從同步期間新的請求處理,主庫會在內存中生成一個緩衝區(replication buffer)來記錄主從同步期間的新的寫操作。
(4)從庫獲取主庫發送過來的replicatio buffer緩衝區,開始執行新的寫請求,同步數據。
以上就是主從第一次同步數據時的過程。第一次同步數據主要依賴於主庫的rdb日誌文件,後續的數據同步主要依賴於replication buffer緩衝區。
同步數據流程圖如下所示:
以上就是第一次主從同步數據的總體流程,在圖中我們可看到主實例接受到psync請求後,會進行FULLRESYNC響應,該響應傳遞了兩個參數:
{runID}:主庫的唯一標記。
{offset}:複製進度,如果是-1表示第一次複製。
replication buffer注意點:
主實例會為每一個從實例都分配一個複製緩衝區,並不是所有的從實例共用一個緩衝區。
緩衝區的設置需要合理,因為這是一個複製緩衝區所以畢竟會有大小限制,複製緩衝區如果一旦因為寫入的命令過多導致溢出,那麼主實例會直接關閉和從實例的連接,導致主從同步失敗。
出現複製緩衝區溢出情況,可能在於全量複製時,從庫中加載主庫的rdb文件較慢,同時主節點接收了大量的寫命令,導致寫命令積累大小超過了緩衝區大小出現溢出。
解決方案:
(1)控制主節點數據的大小,一般將主節點的數據量控制在2~4GB即可,可以讓從節點加載全量的rdb文件時執行的快些。
(2)合理設置複製緩衝區大小,命令 - config set client-output-buffer-limit slave 512mb 128mb 60。
第一個參數512mb表示緩衝區大小的上限為512mb。
第二個、第三個參數128mb和60表示如果連續60秒內的寫入量超過128mb也會造成緩衝區溢出。
可以根據以上命令來推斷負載情況,來合理設置緩衝區大小。比如上述命令(假設每條命令大小是1kb):
複製緩衝區可以接受512mb / 1kb * 1000 = 512K條寫命令。
可接受的寫命令速率上限為128mb / 1kb * 1000 / 60s = 2000條/s。
(3)避免出現規模太大的從節點集群。因為主節點上複製緩衝區的總內存開銷 = 所有從節點的緩衝區內存之和,從而減少主節點的使用內存。
三、主從從模式的數據同步
為什麼需要主從從模式?我們先來分析下,主從集群下會有什麼性能瓶頸。
對於一個主從庫來說,需要完成兩個耗時操作,生成rdb文件和傳輸rdb文件。雖然rdb文件是異步生成的,但是還是得通過主線程來fork一個子進程來異步創建rdb文件,但是fork這個步驟會阻塞主線程,如果從實例很多的情況下,並每個從結點都需要進行全量複製,那麼這個fork就會變的很慢了。
傳輸rdb文件在於主實例傳輸文件到從實例也會佔用主庫的網絡帶寬給主庫資源帶來壓力。
所以使用了主從從模式來減少從實例過多帶給主實例的影響。
主從從模式的工作圖:
將部分從庫和其中一個從庫建立主從關係,並且那個從庫會和主庫建立主從關係。這樣全量複製時的壓力就會被分擔到那個有級聯主從關係的從庫中,減少了主庫的壓力。
四、增量複製緩衝區
redis引入增量複製緩衝區的概念還是在2.8之前,因為之前如果redis出現了主從之間的網絡閃斷,那麼恢復後,從庫需要重新進行一遍全量複製來進行主從同步,增大開銷。
有了增量複製緩衝區之後,如果主從之間檢測到網絡閃斷,從庫恢復後就通過增量複製緩衝區來找到未同步的位置,來繼續同步閃斷期間內的數據。
· repl_backlog_buffer
repl_backlog_buffer即增量複製緩衝區,他其實是一個環形的緩衝區,所以會有2個指針分別是主庫寫的位置和從庫讀的位置,還有一個偏移量,代表從庫還有多少數據沒用讀取完畢。
剛開始主庫寫的位置和從庫讀的位置在同一位置,隨著寫命令的請求,主庫寫的位置就會不斷變大。每個從庫通過從庫的讀位置來判斷從庫閃斷前的位置,最終從庫在後續恢復後複製完寫操作命令後,他在緩衝區中的讀的位置應該與主庫寫的位置相等。
如果出現從庫閃斷前的位置的數據被新一輪寫入的數據覆蓋 即環形緩衝區被主庫的寫命令所覆蓋了,那麼在從庫恢復後還是得重新做一次全量的數據主從同步。
我們通常可以適當調大一些repl_backlog_size的參數。
repl_backlog_size = 緩存空間大小 * 2。
緩存空間大小 = 主庫寫入命令 * 操作大小 - 主從庫網絡傳輸命令速度 * 操作大小。
比如,主庫每秒寫入2000個操作,每個操作大小為1KB,網絡每秒能傳輸1000個操作,那麼緩存空間大小 = 2000 * 1 - 1000 * 1 = 1000kb = 1mb。
所以為了應對突發的情況,需要設置repl_backlog_size 為2mb。
· 網絡閃斷恢復時主從數據同步工作流程
主從庫恢復後,從庫首先會給主庫發送psync命令,並把自己當前的salve_repl_offset(從庫在增量複製緩衝區讀的位置)發給主庫,主庫會判斷master_repl_offset(主庫在增量複製緩衝區寫的位置)和salve_repl_offset之間的偏移量,然後把他們之間的命令操作通過複製緩衝區同步給從庫。
五、總結
本文主要分析了redis在運行期間通過主從模式來保證高可用。而使用主從模式進行讀寫分離的好處:避免了加鎖的開銷。
為了實現redis的高可用性,就會有多個redis實例,為了保證實例數據的最終一致性,主從數據同步中我們需要關心兩個緩衝區。
(1)replication buffer:主實例和從實例數據同步時的複製緩衝區。
(2)repl_backlog_buffer:從庫斷開之後,找到主從差異數據的環形緩衝區(增量複製緩衝區),減少主從閃斷恢復時全量恢復數據的開銷。
不過單單的採用主從模式來保證高可用還是有很多弊端的,比如主實例故障的情況,因此redis的高可用性還需要哨兵集群來幫助,哨兵機制會在下一篇來進行分析。
問題一:那麼主從庫間的複製為什麼不使用aof文件
假設要使用aof做全量同步,意味著必須打開aof功能,就要選擇aof刷盤策略,選擇不當會嚴重影響redis性能,而rdb只有在需要同步時才會觸發一次快照,對於一些redis數據不敏感的讀緩存模式的業務,其實不需要那麼依賴aof。
所以在主從間複製使用aof文件,主要原因在於從庫恢復數據特別快,時間成本最低。
另外aof日誌文件記錄的是每一次寫操作的命令,寫操作很多文件就會很大。而rdb文件內容是經過壓縮的二進位文件數據,文件很小,這也是aof文件在網絡傳輸時候的一個優勢。
問題二:replication buffer和repl_backlog_buffer的區別
replication buffer是用在主從之間發送數據時或全量同步數據時的緩衝區,並且每個從庫都會生成一個複製緩衝區。
repl_backlog_buffer是用來持續保持寫操作的一塊緩衝區,多個從庫共享一個增量緩衝區,在主庫啟動後就會創建出來。不同的從庫增量複製進度通過從庫讀取的指針和主庫寫的指針來控制。
當出現主從閃斷,恢復後恢復數據時,如果出現從庫需要恢復的歷史數據在增量複製緩衝區中已經被覆蓋(因為是增量複製緩衝區是環形緩衝區),那麼就需要重新進行全量複製。
參考資料 - 《Redis核心技術與實戰》(數據同步:主從庫如何實現數據一致)