4 增量複制
- 如果全量複制過程中,M-R網絡連接配接中斷,那麼salve重連M時,會觸發增量複制
- M直接從自己的backlog中擷取部分丢失的資料,發送給R node
- msater就是根據R發送的psync中的offset來從backlog中擷取資料的

5 M關閉持久化時的複制安全性
在使用 Redis 複制功能時的設定中,推薦在 M 和 R 中啟用持久化。
當不可能啟用時,例如由于非常慢的磁盤性能而導緻的延遲問題,應該配置執行個體來避免重新開機後自動重新開始複制。
關閉持久化并配置了自動重新開機的 M 是危險的:
- 設定節點 A 為 M 并關閉它的持久化設定,節點 B 和 C 從 節點 A 複制資料
- 節點 A 當機,但它有一些自動重新開機系統可重新開機程序。但由于持久化被關閉了,節點重新開機後其資料集是空的!
- 這時B、C 會從A複制資料,但A資料集空,是以複制結果是它們會銷毀自身之前的資料副本!
當 Redis Sentinel 被用于高可用并且 M 關閉持久化,這時如果允許自動重新開機程序也是很危險的。例如, M 可以重新開機的足夠快以緻于 Sentinel 沒有探測到故障,是以上述的故障模式也會發生。
任何時候資料安全性都是很重要的,是以如果 M 使用複制功能的同時未配置持久化,那麼自動重新開機程序這項就該被禁用。
用Redis主從同步,寫入Redis的資料量太大,沒加頻次控制,導緻每秒幾十萬寫入,主從延遲過大,運維頻頻報警,在主庫不挂掉的情況下,這樣大量寫入會不會造成資料丢失?
若主從延遲很大,資料會堆積到redis主庫的發送緩沖區,會導緻主庫OOM。
6 複制工作原理
每個 M 都有一個 replication ID :一個較大的僞随機字元串,标記了一個給定的資料集。
每個 M 也持有一個偏移量,M 将自己産生的複制流發送給 R 時,發送多少個位元組的資料,自身的偏移量就會增加多少,目的是當有新的操作修改自己的資料集時,它可據此更新 R 的狀态。
複制偏移量即使在沒有一個 R 連接配接到 M 時,也會自增,是以基本上每一對給定的
Replication ID, offset
都會辨別一個 M 資料集的确切版本。
psync
R使用
psync
從M複制,psync runid offset
M會根據自身情況傳回響應資訊:
- 可能是FULLRESYNC runid offset觸發全量複制
- 可能是CONTINUE觸發增量複制
R 連接配接到 M 時,它們使用 PSYNC 指令來發送它們記錄的舊的 M replication ID 和它們至今為止處理的偏移量。通過這種方式, M 能夠僅發送 R 所需的增量部分。
但若 M 的緩沖區中沒有足夠的指令積壓緩沖記錄,或者如果 R 引用了不再知道的曆史記錄(replication ID),則會轉而進行一個全量重同步:在這種情況下, R 會得到一個完整的資料集副本,從頭開始。即:
- 若R重連M,那麼M僅會複制給R缺少的部分資料
- 若第一次連接配接M,那麼會觸發全量複制
Redis使用複制保證資料同步,以2.8版本為界:
2.8前性能較差的複制和指令傳播
首先是從伺服器發生同步操作sync,主伺服器執行bgsave生成一個全量RDB檔案,然後傳輸給從伺服器。
同時主伺服器會把這一過程中執行的寫指令寫入緩存區。從伺服器會把RDB檔案進行一次全量加載。
加載完畢後,主伺服器會把緩存區中的寫指令傳給從伺服器。從伺服器執行指令後,主從伺服器的資料就一緻了。
這種方式每次如果網絡出現故障,故障重連後都要進行全量資料的複制。對主伺服器的壓力太大,也會增加主從網絡傳輸的資源消耗。
2.8後的優化
增加部分重同步功能,就是同步故障後的一部分資料,而非全量資料。這種優化在量級非常大的情況下效率提升很明顯。
4.0的PSYNC2
7 複制的完整流程
R如果跟M有網絡故障,斷開連接配接會自動重連。
M如果發現有多個R都重新連接配接,僅會啟動一個rdb save操作,用一份資料服務所有R。
- R啟動,僅儲存M的資訊,包括M的
和host
,但複制流程尚未開始M host和ip配置在ip
中的 Rofredis.conf
- R内部有個定時任務,每s檢查是否有新的M要連接配接和複制,若發現,就跟M建立socket網絡連接配接。
- R發送ping指令給M
- 密碼認證 - 若M設定了requirepass,那麼salve必須同時發送Mauth的密碼認證
- M 第一次執行全量複制,将所有資料發給R
- M後續持續将寫指令,異步複制給R
heartbeat
主從節點互相都會發送heartbeat資訊。
M預設每隔10秒發送一次heartbeat,salve node每隔1秒發送一個heartbeat。
8 斷點續傳
Redis 2.8開始支援主從複制的斷點續傳
主從複制過程,若網絡連接配接中斷,那麼可以接着上次複制的地方,繼續複制下去,而不是從頭開始複制一份。
M和R都會維護一個offset
- M在自身基礎上累加offset,R亦是
- R每秒都會上報自己的offset給M,同時M儲存每個R的offset
M和R都要知道各自資料的offset,才能知曉互相之間的資料不一緻情況。
backlog
M會在記憶體中維護一個backlog,預設1MB。M給R複制資料時,也會将資料在backlog中同步寫一份。
backlog主要是用做全量複制中斷時候的增量複制。
M和R都會儲存一個replica offset還有一個M id,offset就是儲存在backlog中的。若M和R網絡連接配接中斷,R會讓M從上次replica offset開始繼續複制。但若沒有找到對應offset,就會執行resynchronization。
M run id
- info server,可見M run id
根據host+ip定位M node,是不靠譜的,如果M node重新開機或者資料出現了變化,那麼R node應該根據不同的run id區分,run id不同就做全量複制。
如果需要不更改run id重新開機redis,可使用:
redis-cli debug reload
9 無磁盤化複制
M在記憶體中直接建立RDB,然後發送給R,不會在自己本地持久化。
隻需要在配置檔案中開啟
repl-diskless-sync yes
即可.
等待 5s 再開始複制,因為要等更多 R 重連
repl-diskless-sync-delay 5
10 處理過期key
Redis 的過期機制可以限制 key 的生存時間。此功能取決于 Redis 執行個體計算時間的能力,但是,即使使用 Lua 腳本更改了這些 key,Redis Rs 也能正确地複制具有過期時間的 key。
為實作功能,Redis 不能依靠主從使用同步時鐘,因為這是一個無法解決的問題并且會導緻 race condition 和資料不一緻,是以 Redis 使用三種主要的技術使過期的 key 的複制能夠正确工作:
R 不會讓 key 過期,而是等待 M 讓 key 過期。當一個 M 讓一個 key 到期(或由于 LRU 删除)時,它會合成一個 DEL 指令并傳輸到所有 R
但由于這是 M 驅動的 key 過期行為,M 無法及時提供 DEL 指令,是以有時 R 的記憶體中仍可能存在邏輯上已過期的 key 。為處理該問題,R 使用它的邏輯時鐘以報告隻有在不違反資料集的一緻性的讀取操作(從主機的新指令到達)中才存在 key。用這種方法,R 避免報告邏輯過期的 key 仍然存在。在實際應用中,使用 R 程式進行縮放的 HTML 碎片緩存,将避免傳回已經比期望的時間更早的資料項
在Lua腳本執行期間,不執行任何 key 過期操作
當一個Lua腳本運作時,概念上講,M 中的時間是被當機的,這樣腳本運作的時候,一個給定的鍵要麼存在or不存在。這可以防止 key 在腳本中間過期,保證将相同的腳本發送到 R ,進而在二者的資料集中産生相同的效果。
一旦 R 被提升 M ,它将開始獨立過期 key,而不需要任何舊 M 幫助。
11 重新啟動和故障轉移後的部分重同步
Redis 4.0 開始,當一個執行個體在故障轉移後被提升為 M 時,它仍然能夠與舊 M 的 R 進行部分重同步。為此,R 會記住舊 M 的舊 replication ID 和複制偏移量,是以即使詢問舊的 replication ID,也可以将部分複制緩沖提供給連接配接的 R 。
但是,更新的 R 的新 replication ID 将不同,因為它構成了資料集的不同曆史記錄。例如,M 可以傳回可用,并且可以在一段時間内繼續接受寫入指令,是以在被提升的 R 中使用相同的 replication ID 将違反一對複制辨別和偏移對隻能辨別單一資料集的規則。
另外,R 在關機并重新啟動後,能夠在 RDB 檔案中存儲所需資訊,以便與 M 進行重同步。這在更新的情況下很有用。當需要時,最好使用 SHUTDOWN 指令來執行 R 的儲存和退出操作。
參考