淺析Redis複製過程

摘要

Redis默認使用異步複製,其特點是低延遲和高性能。異步複製就意味着在故障轉移期間,有丟失數據的風險。你可以參考Redis 集羣文檔,瞭解關於高可用性和故障轉移的信息,本文主要討論Redis 複製功能的基本特性。

複製

在 Redis 複製的基礎上,使用和配置主從複製非常簡單,能使得從 Redis 服務器(Slave)能精確得複製主 Redis 服務器(Master)的內容。每次當 Slave 和 Master 之間的連接斷開時, Slave 會自動重連到 Master 上,並且無論這期間 Master 發生了什麼, Slave 都將嘗試讓自身成爲 Master 的精確副本。

複製的運行依靠三個主要的機制:

  • 當一個 Master 實例和一個 Slave 實例連接正常時, Master 將自身數據集的改變以命令流的方式保持對 Slave 的更新,包括客戶端的寫入、key 的過期或被逐出等等。
  • 當 Master 和 Slave 之間的連接斷開之後,因爲網絡問題、或者是主從連接超時, Slave 重新連接上 Master 並會嘗試進行部分重同步:這意味着它會嘗試只獲取在斷開連接期間內丟失的命令流。
  • 當無法進行部分重同步時, Slave 會請求進行全量重同步。這會涉及到一個更復雜的過程,Master 需要創建所有數據的快照,將之發送給 Slave ,之後在數據集更改時持續發送命令流到 Slave 。

以下是一些關於複製的重要事項:

  • 異步複製

  • 一個Master可以對應多個Slave

  • Slave 可以接受其他 Slave 的連接

  • 複製在 Master 側是非阻塞的

  • 複製在 Slave 側大部分時間也是非阻塞的

    在初次同步之後,舊數據集必須被刪除,同時加載新的數據集。 Slave 在這個短暫的時間窗口內會阻塞到來的連接請求。

  • 可以使用複製來避免 Master 將全部數據集寫入磁盤造成的開銷

    一種典型的例子是Master 關閉持久化,然後連接一個 Slave ,其配置爲RDB或者 AOF。但是,這個設置必須小心處理,因爲重新啓動的 Master 程序將從一個空數據集開始:如果一個 Slave 試圖與它同步,那麼這個 Slave 也會被清空。

當 Master 關閉持久化時,複製的安全性

在使用 Redis 複製功能的設置中,強烈建議在 Master 和 Slave 中啓用持久化。當不可能啓用時,例如由於磁盤性能而導致的延遲問題,應該通過設置避免maste故障後自動重啓

通過下面例子,可以更好地理解關閉了持久化並配置了自動重啓的 Master 是危險的:

  1. 設置節點 A 爲 Master 並關閉它的持久化設置,節點 B 和 C 從 節點 A 複製數據。
  2. 節點 A 崩潰,但是可能有自動重啓的運維繫統可以重啓進程。但是由於持久化被關閉了,節點重啓後其數據集合爲空。
  3. 節點 B 和 節點 C 會從節點 A 複製數據,但是節點 A 的數據集是空的,因此複製的結果是BC會銷燬自身之前的數據副本。

在這裏插入圖片描述

任何時候數據安全性都是很重要的,如果 Master 使用複製功能的同時未配置持久化,那麼自動重啓進程這項應該被禁用。

複製的工作原理

每一個 Redis Master 都有一個 replication ID :這是一個較大的僞隨機字符串,標記了一個給定的數據集。每個 Master 持有一個偏移量,Master 將自己產生的複製流發送給 Slave 時,發送多少個字節的數據,自身的偏移量就會增加多少,目的是當有新的操作修改自己的數據集時,它可以以此更新 Slave 的狀態。複製偏移量即使在沒有一個 Slave 連接到 Master 時,也會自增,所以基本上每一對給定的(Replication ID, offset)都會標識一個 Master 數據集的確切版本。

當 Slave 連接到 Master 時,它們使用 PSYNC 命令來發送它們記錄的舊的 Master replication ID 和它們至今爲止處理的偏移量。通過這種方式, Master 能夠僅發送 Slave 所需的增量部分。但是如果 Master 的緩衝區中沒有足夠的命令積壓緩衝記錄,或者如果 Slave 引用了不再知道的歷史記錄(replication ID),則會轉而進行一個全量重同步:在這種情況下, Slave 會得到一個完整的數據集副本,從頭開始。

全量重同步的詳細過程:

Master 開啓一個後臺保存進程,以便於生產一個 RDB 文件。同時它開始緩衝所有從客戶端接收到的新的寫入命令。當後臺保存完成時, Master 將數據集文件傳輸給 Slave, Slave將之保存在磁盤上,然後加載文件到內存。再然後 Master 會發送所有緩衝的命令發給 Slave。這個過程以指令流的形式完成。

下圖是一個簡單的同步例子:

在這裏插入圖片描述
針對上圖情況的進一步說明:

  • Master A在長期的運行過程中,可能會遇到重啓或者是由故障轉移提升爲Master的,所以它的緩存區可能存在多對(Replication ID, offset)。
  • 當Slave B首次連接到Master A時,發送PSYNC(290069241989391187,0)給Master A,Master A收到請求後找到replication ID:290069241989391187的數據集,把offset:0~22處的數據集發送給Slave B,Master A的偏移量自增22,Slave B收到Master A返回的命令流後,逐一執行,並修改offset爲22。
  • Slave C發送PSYNC(2900692419893910001,33)給Master A,Master A發現replication ID:2900692419893910001不存在緩衝區中,則向Slave C發送全量數據(RDB文件),這樣Slave C就能得到一個完整的數據,對於Master A以後的同步,採用和Slave B中描述的同步方式。

配置

配置基本的 Redis 複製功能很簡單:只需要將以下內容加進 Slave 的配置文件:

# slaveof <masterip> <masterport>
slaveof 192.168.33.160 6379

# masterauth <master-password>
masterauth 123456

當然你也可以使用 SLAVEOF 命令,

只讀 Slave

redis.conf 文件中的 slave-read-only 變量控制只讀,自從Redis 2.6以後,該值默認yes,且可以在運行時使用 CONFIG SET slave-read-only來隨時開啓或者關閉。只讀模式下的 Slave 將會拒絕所有寫入命令。redis.conf 文件中使用 rename-command 指令可以禁用 CONFIG等管理員命令以提高只讀實例的安全性。

需要注意的是,從Redis 4.0開始,Slave的數據傳播將從最頂層Master向低層所有Slave發送相同的命令流。例如,A -> B -> C,B和C的複製都是從A中獲取命令流。

寫入Master

只有當至少有 N 個 Slave滯後小於 M 秒,Redis Master 的寫入操作纔會成功,否則,返回 error。

N:slave 數量;

M:秒數;

Master可以配置這兩個配置參數:

  • min-slaves-to-write <slave 數量>
  • min-slaves-max-lag <秒數>

以下是該特性的工作原理:

  • Redis Slave 每秒鐘都會 ping Master,確認已處理的複製流的數量。
  • Redis Master 會記得上一次從每個 Slave 都收到 ping 的時間。
  • Master通過判斷是否至少有 N 個 Slave滯後小於 M 秒,來確定寫入還是返回error。

這是一個盡最大努力保證數據安全的機制,對於寫入來說,不能保證一致性,但至少數據丟失的時間窗限制在指定的秒數內。

Slave如何處理key的過期

爲了實現Slaves能正確地複製具有過期時間的 key,Redis 使用三種主要的技術:

  • Slave 不會讓 key 過期,而是等待 Master 讓 key 過期。當一個 Master 讓一個 key 到期(或由於 LRU 算法將之驅逐)時,它會合成一個 DEL 命令並傳輸到所有的 Slave。
  • 但是,由於這是 Master 驅動的 key 過期行爲,Master 無法及時提供 DEL 命令,所以有時候 Slave 的內存中仍然可能存在在邏輯上已經過期的 key 。爲了處理這個問題,Slave 使用它的邏輯時鐘,只用於不違反數據集一致性的讀取操作。(也就是說Master的命令流能夠識別改key存在,其他的任何讀取操作無法讀到該key。)
  • 在Lua腳本執行期間,不執行任何 key 過期操作。當一個Lua腳本運行時,從理論上講,Master 中的時間是被凍結的,這樣腳本運行的時候,一個給定的鍵要麼存在要麼不存在。

需要注意的是,一旦Slave 被提升爲一個 Master,它將獨立地依靠本地時鐘處理key的過期,而不需要任依賴Master。

參考

[1] Replication.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章