Redis主從復制是如何保證數據不丟失的?
介紹
在生產環境中,為了系統的可靠性,我們會對Redis搭建主從。這樣當一個實例發生宕機,另一個實例中還有數據,還能繼續提供服務。主從庫之間采用的是讀寫分離的模式。
讀操作:主庫,從庫都可以執行 寫操作:只能主庫上執行,主庫將操作同步給從庫
因為主從庫都可以接收讀請求,提高了系統的QPS。那么主從庫之間如何進行數據同步呢?
全量復制
「我們可以通過replicaof命令或者replicaof設置來讓redis形成主從庫的關系」(redis 5.0之前使用slaveof命令)
假設現在有兩個實例,實例一(172.16.19.1)和實例二(172.16.19.2)
當我們在實例二上執行如下命令后,實例二就變成了實例一的從庫,并從實例一上復制數據
- replicaof 172.16.19.1 6379
當然我們也可以在實例二的redis.conf配置文件中配置如下內容
- replicaof 172.16.19.1 6379
整個同步過程如下圖所示
主從庫全量復制主要分為如下三個階段
- 從庫發送psync命令,此時主庫開始生成rdb文件
- 主庫將生成的rdb文件發送給從庫
- 主庫將生成rdb文件后接收到的寫命令發送給從庫
我們仔細分析一下三個過程
從庫發送psync命令,此時主庫開始生成rdb文件
從庫發送psync命令,表示要進行數據復制,psync命令包含了如下2個參數
「runID」:主庫的runID,每個redis實例啟動時都會自動生成一個隨機ID,用來唯一標識實例。當從庫第一次復制時,因為不知道主庫的runID,所以將runID設置為?「offset」:復制進度,第一次復制為-1
主庫將生成的rdb文件發送給從庫
主庫執行bgsave命令,生成rdb文件,并且發送給從庫。從庫收到rdb文件后,會清空當前數據庫,然后加載rdb文件。因為從庫在通過replicaof命令復制前,可能保存了其他的數據,為了避免之前數據的影響,需要先把從庫清空
主庫將生成rdb文件后接收到的寫命令發送給從庫
生成rdb文件后,主庫仍能執行寫命令,這些寫命令會被放到replication buffer中。當主庫發送完rdb文件后,就會把replication buffer中的命令發給從庫,從庫執行這些操作后。主從就是實現同步了。「后續正常的命令同步也是主庫將命令寫到replication buffer然后發給從庫」
增量復制
如果在主從命令傳播的過程中,出現了網絡異常應該怎么辦呢?
在Redis2.8之前,如果出現了網絡異常,從庫和主庫會進行一次增量復制,開銷非常大。在Redis2.8之后,主從庫會采用增量復制的方式進行同步。增量復制只會把主從庫斷連期間主庫接收到的命令同步給從庫
「增量同步時主從庫如何保持一致呢?」
復制偏移量
主庫和存庫都會在內部維護一個復制偏移量 主庫每次向從庫發送n個字節的數據時,就把自己的復制偏移量加上n 從庫每次收到主庫傳來的n個字節的數據時,就把自己的復制偏移量加上n
repl_backlog_buffer(復制積壓緩沖區)
repl_backlog_buffer是由主服務器維護的一個固定長度先進先出(FIFO)隊列 我們舉個例子,如果將hello字符串放入一個固定長度為3的FIFO隊列,值依次為
- [h, e, l] [e, l, l] [l, l, o]
「每次都是都是在隊尾添加值,彈出隊首」復制積壓緩沖區的構造如下
偏移量 | ... | 20 | 21 | 22 | 23 | 24 | 25 | ... |
---|---|---|---|---|---|---|---|---|
字節值 | ... | h | e | l | l | l | o | ... |
「當服務器在進行命令傳播的時候,不僅會將寫命令發送給所有從服務器,還會將寫命令入隊到復制積壓緩沖區中」
當從庫發生網絡中斷重新上主庫之后,會發送「psync 主庫id offset」給主庫,主庫根據復制偏移量來決定對從服務器執行何種復制操作
如果從庫發送的主庫id與當前連接的主庫id相同,可以繼續嘗試增量復制
如果從庫發送的主庫id與當前連接的主庫id不相同,說明主服務器斷線之前復制的主服務器并不是當前連接的服務器,只能全量復制
如果offse偏移量之后的數據(即偏移量offset+1開始的數據)仍然存在repl_backlog_buffer中,則把命令放到replication buffer,然后發送給從庫
如果offset偏移量之后的數據不存在repl_backlog_buffer中,則進行全量復制
replication buffer和repl_backlog_buffer
有很多小伙伴剛開始的時候分不清replication buffer和repl_backlog_buffer的作用,包括我。
其實很好理解,replication buffer其實是一個client端的緩沖區,redis每次把要發送的命令放到這個緩沖區中,然后再發送。「每個客戶端一個replication buffer」
「而repl_backlog_buffer單純用作增量復制,在redis服務器中只有一個」
本文轉載自微信公眾號「Java識堂」,可以通過以下二維碼關注。轉載本文請聯系Java識堂公眾號。