網站首頁 編程語言 正文
介紹 Redis 的復制
Redis 的復制功能分為同步(sync)和命令傳播(command propagate)這兩個操作
- 同步操作用于,將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態;
- 命令傳播操作用于,在主服務器的數據庫狀態被修改,導致主從服務器的數據庫狀態出現不一致時,讓主從服務器的數據庫重新回到一致狀態。
如果主從服務器雙方的數據庫保存相同的數據,我們稱主從服務器的數據庫狀態一致
當從服務器第一次連接主服務器時,Redis 使用全量復制進行數據同步。
當從服務器在斷線后重新連接主服務器時,Redis 使用增量復制進行數據同步。
完整重同步
全量復制,也被稱為完整重同步。
當客戶端向從服務器發送 slaveof 命令,要求從服務器復制主服務器時,從服務器首先需要執行同步操作,將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態。
從服務器對主服務器的完整重同步操作,需要通過向主服務器發送 psync 命令來完成。psync 的命令為:psync ? -1
psync 命令在完整重同步模式下的的執行步驟:讓主服務器創建并發送 RDB 文件,以及主服務器向從服務器發送保存在緩沖區里面的寫命令來進行同步。
- 從服務器向主服務器發送 psync 命令。
- 主服務器收到 psync 命令后,主服務器執行 bgsave 命令,在后臺生成一個 RDB 文件,并使用一個緩沖區(replication buffer)記錄從現在開始執行的所有寫命令。
- 主服務器給從服務器同步數據:當主服務器的 bgsave 命令執行完畢時,主服務器會將 bgsave 命令生成的 RDB 文件發送給從服務器,從服務器接收并載入這個 RDB 文件,將自己的數據庫狀態更新至主服務器執行 bgsave 命令時的數據庫狀態。
- 主服務器給從服務器發送緩沖區里面的所有寫命令:主服務器將記錄在緩沖區里面的所有寫命令發送給從服務器, 從服務器執行這些寫命令,將自己的數據庫狀態更新至主服務器數據庫當前所處的狀態。
需要注意的是:
從庫在開始和主庫進行數據復制前,可能保存了其他數據。為了避免之前數據的影響,從庫在收到主庫發送的 RDB 文件后,會先把自己當前的數據庫清空。
介紹 偏移量 & 積壓緩沖區 & 運行ID
部分重同步功能通過以下三個部分來實現:
- 主服務器的復制偏移量 和 從服務器的復制偏移量(replication offset)
- 主服務器的復制積壓緩沖區(replication backlog buffer)
- 服務器的運行 ID(run ID)
復制偏移量
主服務器和從服務器會分別維護一個復制偏移量:
- 主服務器每次向從服務器傳播 N 個字節的數據時,就將自己的復制偏移量的值加上 N。
- 從服務器每次收到主服務器傳播來的 N 個字節的數據時,就將自己的復制偏移量的值加上 N。
通過對比主從服務器的復制偏移量,程序可以很容易地知道主從服務器是否處于一致狀態:
- 如果主從服務器兩者的偏移量總是相同,那么說明主從服務器處于一致狀態。
- 如果主從服務器兩者的偏移量并不相同,那么說明主從服務器并未處于一致狀態。
復制積壓緩沖區
復制積壓緩沖區(repl_backlog_buffer)是由主服務器維護的一個固定長度的先進先出(FIFO)隊列。
固定指的是,當入隊元素的數量大于隊列長度時,最先入隊的元素會被彈出,而新元素會被放入隊列。或者理解復制積壓緩沖區為一個環形緩沖區。
當主服務器進行命令傳播時,它不僅會將寫命令發送給所有從服務器,還會將寫命令入隊到復制積壓緩沖區里面。
因此,主服務器的復制積壓緩沖區里面會保存著一部分最近傳播的寫命令,并且復制積壓緩沖區會為隊列中的每個字節記錄相應的復制偏移量。
當從服務器在斷線后重新連接主服務器時,從服務器會通過 psync 命令將自己的復制偏移量 offset 發送給主服務器,主服務器會根據這個復制偏移量來決定對從服務器執行完整重同步還是部分重同步操作:
- 如果 offset 偏移量之后的數據(也即是偏移量 offset+1 開始的數據)仍然存在于復制積壓緩沖區里面,那么主服務器將對從服務器執行部分重同步操作。
- 如果 offset 偏移量之后的數據已經不存在于復制積壓緩沖區,那么主服務器將對從服務器執行完整重同步操作。
復制積壓緩沖區的大小
Redis 為復制積壓緩沖區設置的默認大小為 1MB,如果主服務器需要執行大量的寫命令,又或者主從服務器斷線后重連接所需的時間比較長,那么這個大小也許并不合適。我們可以通過 repl-backlog-size 選項修改復制積壓緩沖區的大小。
如果復制積壓緩沖區的大小設置得不恰當,那么 psync 命令的部分重同步復制就不能正常發揮作用。因此,正確估算和設置復制積壓緩沖區的大小非常重要。
為了保證主從服務器斷線并重連接后可以使用部分重同步功能,我們需要保證復制積壓緩沖區的大小足夠大。復制積壓緩沖區的最小大小可以根據公式 second * write_size_per_second 來估算:
- second 是從服務器斷線后重新連接上主服務器所需的平均時間(以秒計算)。
- write_size_per_second 是主服務器平均每秒產生的寫命令數據量(協議格式的寫命令的長度總和)。
例如,如果主服務器平均每秒產生1 MB的寫數據,而從服務器斷線之后平均要 5 秒才能重新連接上主服務器,那么復制積壓緩沖區的大小就不能低于 5 MB。
為了安全起見,可以將復制積壓緩沖區的大小設為: 2 * second * write_size_per_second,這樣可以保證絕大部分斷線情況都能用部分重同步來處理。
服務器運行 ID
每個 Redis 服務器,不論主服務器還是從服務,都會有自己的運行 ID。運行 ID 在服務器啟動時自動生成,由 40 個隨機的十六進制字符組成,例如:53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3。
當從服務器對主服務器進行初次復制時,主服務器會將自己的運行 ID 發送給從服務器,而從服務器會將主服務器的這個運行 ID 保存起來。 當從服務器斷線并重新連上一個主服務器時,從服務器將向當前連接的主服務器發送之前保存的主服務器的運行 ID:
- 如果從服務器保存的主服務器的運行 ID 和當前連接的主服務器的運行 ID 相同,那么說明從服務器斷線之前復制的就是當前連接的這個主服務器, 主服務器可以繼續嘗試執行部分重同步操作。
- 如果從服務器保存的主服務器的運行 ID 和當前連接的主服務器的運行 ID 并不相同,那么說明從服務器斷線之前復制的主服務器并不是當前連接的這個主服務器,主服務器將對從服務器執行完整重同步操作。
部分重同步
增量復制,也被稱為部分重同步。
在 Redis 中,從庫對主庫的復制可以分為以下兩種情況:
- 初次復制:從庫以前沒有復制過任何主庫,或者從庫當前要復制的主服務器和上一次復制的主服務器不同。
- 網絡斷線重連后復制:處于命令傳播階段的主從庫因為網絡原因而中斷了復制,但從庫通過自動重連接重新連上了主庫,并繼續復制主服。
在 Redis 2.8 之前,如果主從庫在命令傳播時出現了網絡中斷,那么在斷線重連后,從庫會和主庫重新進行一次全量復制,開銷非常大。
從 2.8 版本開始,Redis 引入了部分重同步功能。部分重同步指的是,從服務器只同步主服務器的部分數據。當從服務器在斷線后重新連接主服務器時,如果條件允許,主服務器可以將主從服務器連接斷開期間執行的寫命令發送給從服務器,從服務器只要接收并執行這些寫命令,就可以將數據庫更新至主服務器當前所處的狀態。
執行部分重同步是有前提條件的。
- offset 偏移量
- 運行 ID
當從服務器對主服務器進行初次復制時,主服務器會將自己的運行 ID 發送給從服務器,而從服務器會將主服務器的這個運行 ID 保存起來。 當從服務器斷線并重新連上一個主服務器時,從服務器會通過 psync 命令將自己的復制偏移量 offset 和 之前保存的主服務器的運行 ID 發送給主服務器。
主服務器會根據這個復制偏移量 和 運行ID 來決定對從服務器執行完整重同步還是部分重同步操作:
- 如果從服務器保存的主服務器的運行 ID 和當前連接的主服務器的運行 ID 相同,那么說明從服務器斷線之前復制的就是當前連接的這個主服務器, 主服務器可以繼續嘗試執行部分重同步操作。
- 如果從服務器保存的主服務器的運行 ID 和當前連接的主服務器的運行 ID 并不相同,那么說明從服務器斷線之前復制的主服務器并不是當前連接的這個主服務器,主服務器將對從服務器執行完整重同步操作。
- 如果 offset 偏移量之后的數據(也即是偏移量 offset+1 開始的數據)仍然存在于復制積壓緩沖區里面,那么主服務器將對從服務器執行部分重同步操作。
- 如果 offset 偏移量之后的數據已經不存在于復制積壓緩沖區,那么主服務器將對從服務器執行完整重同步操作。
從服務器對主服務器的部分重同步操作,需要通過向主服務器發送 psync 命令來完成。psync 命令為:psync < runID > < offset >
psync 命令
從服務器對主服務器的同步操作,需要通過向主服務器發送 psync 命令來完成。
psync 命令具有完整重同步(full resynchronization)和部分重同步 (partial resynchronization)兩種模式:
- 完整重同步用于,處理初次復制情況;
- 部分重同步用于,處理斷線后重復制情況:當從服務器在斷線后重新連接主服務器時,如果條件允許,主服務器可以將主從服務器連 接斷開期間執行的寫命令發送給從服務器,從服務器只要接收并執行這 些寫命令,就可以將數據庫更新至主服務器當前所處的狀態。
psync 命令的調用方法有兩種:
- 如果從服務器以前沒有復制過任何主服務器,或者之前執行過 slaveof no one 命令,那么從服務器在開始一次新的復制時將向主服務器發送 psync ? -1 命令,主動請求主服務器進行完整重同步。
- 如果從服務器已經復制過某個主服務器,那么從服務器在開始一次新的復制時將向主服務器發送 psync?命令:其中 runid 是上一次復制的主服務器的運行 ID,而 offset 則是從服務器當前的復制偏移量,接收到這個命令的主服務器會通過這兩個參數來判斷應該對從服務器執行哪種同步操作。
根據情況,接收到 psync 命令的主服務器會向從服務器返回以下三種回復的其中一種:
- 如果主服務器返回 +fullresync?回復,那么表示主服務器將與從服務器執行完整重同步操作:其中 runid 是這個主服務器的運行 ID,從服務器會將這個 ID 保存起來,在下一次發送 psync 命令時使用;而 offset 則是主服務器當前的復制偏移量,從服務器會將這個值作為自己的初始化偏移量。
- 如果主服務器返回 +continue 回復,那么表示主服務器將與從服務器執行部分重同步操作,從服務器只要等著主服務器將自己缺少的那部分數據發送過來就可以了。
- 如果主服務器返回 -err 回復,那么表示主服務器的版本低于 Redis2.8,它識別不了 psync 命令,從服務器將向主服務器發送 sync 命令,并與主服務器執行完整同步操作。
命令傳播
主服務器通過向從服務器傳播命令來更新從服務器的狀態,保持主從服務器一致。
當完成了同步之后, 主從服務器就會進入命令傳播階段, 這時主服務器只要一直將自己執行的寫命令發送給從服務器, 而從服務器只要一直接收并執行主服務器發來的寫命令, 就可以保證主從服務器一直保持一致了。
主服務器進行命令傳播時,它不僅會將寫命令發送給所有從服務器,還會將寫命令入隊到復制積壓緩沖區里面。
心跳檢測
從服務器通過向主服務器發送命令來進行心跳檢測,以及命令丟失檢測。
在命令傳播階段,從服務器默認會以每秒一次的頻率,向主服務器發送命令:replconf ack <replication_offset>。其中 replication_offset 是從服務器當前的復制偏移量。
發送 replconf ack 命令對于主從服務器有三個作用:
- 檢測主從服務器的網絡連接狀態。
- 輔助實現 min-slaves 選項。
- 檢測命令丟失。
檢測主從服務器的網絡連接狀態。
主從服務器可以通過發送和接收 replconf ack 命令來檢查兩者之間的網絡連接是否正常:如果主服務器超過一秒鐘沒有收到從服務器發來的 replconf ack 命令,那么主服務器就知道主從服務器之間的連接出現問題了。
通過向主服務器發送 info replication 命令,在列出的從服務器列表的 lag 一欄中,我們可以看到相應從服務器最后一次向主服務器發送 replconf ack 命令距離現在過了多少秒。在一般情況下,lag 的值應該在 0 秒或者 1 秒之間跳動,如果超過 1 秒的話,那么說明主從服務器之間的連接出現了故障。
輔助實現 min-slaves 選項。
Redis 的 min-slaves-to-write 和 min-slaves-max-lag 兩個選項可以防止主服務器在不安全的情況下執行寫命令。
舉個例子,如果我們向主服務器提供以下設置:
- min-slaves-to-write 3
- min-slaves-max-lag 10
那么在從服務器的數量少于 3 個,或者 3 個從服務器的延遲(lag)值都 ≥ 10 秒時,主服務器將拒絕執行寫命令,這里的延遲值就是上面提到的 info replication 命令的 lag 值。
檢測命令丟失。
如果因為網絡故障,主服務器傳播給從服務器的寫命令在半路丟失,那么當從服務器向主服務器發送 replconf ack 命令時,主服務器將發覺從服務器當前的復制偏移量少于自己的復制偏移量,然后主服務器就會根據從服務器提交的復制偏移量,在復制積壓緩沖區里面找到從服務器缺少的數據,并將這些數據重新發送給從服務器。
參考資料
《Redis設計與實現》
原文鏈接:https://www.cnblogs.com/feiyu2/p/16996233.html
相關推薦
- 2022-07-31 Windows使用cmd命令行查看、修改、刪除與添加環境變量_DOS/BAT
- 2022-04-28 WPF使用Grid網格面板布局_實用技巧
- 2022-11-13 python學習之whl文件解釋與安裝詳解_python
- 2022-10-08 PostgreSQL12.5中分區表的一些操作實例_PostgreSQL
- 2023-01-18 詳解Python中魔法方法的使用_python
- 2022-04-18 C++中的命名空間詳細介紹_C 語言
- 2022-07-19 AI與Python人工智能啟發式搜索概念理解_python
- 2022-07-19 Angular @Inject 注解的實際應用例子和工作原理淺析
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支