讀配置、講原理、看面試真題,我只能幫你到這了。。。

當我在寫一上來就主從、集羣、哨兵,這誰受得了的時候,好多小夥伴就迫不及待的留言想看這些模式了,今天我們就從配置文件、設計原理、面試真題三個方面來聊一聊 Redis 的主從複製。

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

主從複製,從 5.0.0 版本開始,Redis 正式將 SLAVEOF 命令改名成了 REPLICAOF 命令並逐漸廢棄原來的 SLAVEOF 命令

Redis使用默認的異步複製,其特點是低延遲高性能,是絕大多數 Redis 用例的自然複製模式。但是,replica 會異步地確認它從主 master 週期接收到的數據量。

主從拓撲架構

master 用來寫操作,replicas 用來讀取數據,適用於讀多寫少的場景。而對於寫併發量較高的場景,多個從節點會導致主節點寫命令的多次發送從而過度消耗網絡帶寬,同時也加重了 master 的負載影響服務穩定性。

replica 可以接受其它 replica 的連接。除了多個 replica 可以連接到同一個 master 之外, replica 之間也可以像層疊狀的結構(cascading-like structure)連接到其他 replica 。自 Redis 4.0 起,所有的 sub-replica 將會從 master 收到完全一樣的複製流。

當 master 需要多個 replica 時,爲了避免對 master 的性能干擾,可以採用樹狀主從結構降低主節點的壓力。

爲了讓大家對概念有更清晰的認識,我們先來看一下配置文件中主從複製的參數介紹:

REPLICATION

replicaof <masterip> <masterport>

通過設置 master 的 ip 和 port ,可以使當前的 Redis 實例成爲另一臺 Redis 實例的副本。在Redis啓動時,它會自動從 master 進行數據同步。

  • Redis 複製是異步的,可以通過修改 master 的配置,在 master 沒有與給定數量的 replica 連接時,主機停止接收寫入;
  • 如果複製鏈路丟失的時間相對較短,Redis replica 可以與 master 執行部分重新同步,可以使用合理的 backlog 值來進行配置(見下文);
  • 複製是自動的,不需要用戶干預。在網絡分區後,replica 會自動嘗試重新連接到 master 並與 master 重新同步;

masterauth <master-password>

當 master 設置了密碼保護時,replica 服務連接 master 的密碼

replica-serve-stale-data yes

當 replica 與 master 失去連接或者主從複製在進行時,replica 可以有兩種不同的設置:

  • replica-serve-stale-data:yes(默認值),則 replica 仍將響應客戶端請求,可能會有過期數據,或者如果這是第一次同步,則數據集可能爲空。
  • replica-serve-stale-data:no , replica 將對所有請求命令(但不包含 INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST: and LATENCY)返回 SYNC with master in progress 的錯誤。

replica-read-only

可以將 replica 配置爲是否只讀,yes 代表爲只讀狀態,將會拒絕所有寫入命令;no 表示可以寫入。 從 Redis 2.6 之後, replica 支持只讀模式且默認開啓。可以在運行時使用 CONFIG SET 來隨時開啓或者關閉。

對 replica 進行寫入可能有助於存儲一些臨時數據(因爲寫入 replica 的數據在與 master 重新同步後很容易被刪除),計算慢速集或排序集操作並將其存儲到本地密鑰是多次觀察到的可寫副本的一個用例。但如果客戶端由於配置錯誤而向其寫入數據,則也可能會導致問題。

級聯結構中即使 replica B 節點是可寫的,Sub-replica C 也不會看到 B 的寫入,而是將擁有和 master A 相同的數據集。

設置爲 yes 並不表示客戶端用集羣方式以 replica 爲入口連入集羣時,不可以進行 set 操作,且 set 操作的數據不會被放在 replica 的槽上,會被放到某 master 的槽上。

注意:只讀 replica 設計的目的不是爲了暴露於互聯網上不受信任的客戶端,它只是一個防止實例誤用的保護層。默認情況下,只讀副本仍會導出所有管理命令,如CONFIG、DEBUG 等。在一定程度上,可以使用rename-command來隱藏所有管理/危險命令,從而提高只讀副本的安全性

repl-diskless-sync

複製同步策略:磁盤(disk)或套接字(socket),默認爲 no 使用 disk 。

新的 replicas 和重新連接的 replicas 如果因爲接收到差異而無法繼續複製過程,則需要執行“完全同步”。RDB 文件從 master 傳送到 replicas,傳輸可以通過兩種不同的方式進行:

  1. Disk-backed:Redis master 節點創建一個新的進程並將 RDB 文件寫入磁盤,然後文件通過父進程增量傳輸給 replicas 節點;
  2. Diskless:Redis master 節點創建一個新的進程並直接將 RDB 文件寫入到 replicas 的 sockets 中,不寫到磁盤。
  • 當進行 disk-backed 複製時, RDB 文件生成完畢,多個 replicas 通過排隊來同步 RDB 文件。
  • 當進行 diskless 複製時,master 節點會等待一段時間(下邊的repl-diskless-sync-delay 配置)再傳輸以期望會有多個 replicas 連接進來,這樣 master 節點就可以同時同步到多個 replicas 節點。如果超出了等待時間,則需要排隊,等當前的 replica 處理完成之後在進行下一個 replica 的處理。

硬盤性能差,網絡性能好的情況下 diskless 效果更佳

警告:無盤複製目前處於試驗階段

repl-diskless-sync-delay

當啓用 diskless 複製後,可以通過此選項設置 master 節點創建子進程前等待的時間,即延遲啓動數據傳輸,目的可以在第一個 replica 就緒後,等待更多的 replica 就緒。單位爲秒,默認爲5秒

repl-ping-replica-period

Replica 發送 PING 到 master 的間隔,默認值爲 10 秒。

repl-timeout

默認值60秒,此選項用於設置以下情形的 timeout 判斷:

  • 從 replica 節點的角度來看的 SYNC 過程中的 I/O 傳輸 —— 沒有收到 master SYNC 傳輸的 rdb snapshot 數據;
  • 從 replica 節點的角度來看的 master 的 timeout(如 data,pings)—— replica 沒有收到master發送的數據包或者ping;
  • 從 master 節點角度來看的 replica 的 timeout(如 REPLCONF ACK pings)—— master 沒有收到 REPLCONF ACK 的確認信息;
    需要注意的是,此選項必須大於 repl-ping-replica-period,否則在 master 和 replica 之間存在低業務量的情況下會經常發生 timeout。

repl-disable-tcp-nodelay

master 和 replicas 節點的連接是否關掉 TCP_NODELAY 選項。

  • 如果選擇“yes”,Redis 將使用更少的 TCP 數據包和更少的帶寬向 replicas 發送數據。但這會增加數據在 replicas 端顯示的延遲,對於使用默認配置的 Linux 內核,延遲可達40毫秒。
  • 如果選擇“no”,則數據出現在 replicas 端的延遲將減少,但複製將使用更多帶寬。

這個實際影響的是 TCP 層的選項,裏面會用 setsockopt 設置,默認爲 no,表示 TCP 層會禁用 Nagle 算法,儘快將數據發出, 設置爲 yes 表示 TCP 層啓用 Nagle 算法,數據累積到一定程度,或者經過一定時間 TCP 層纔會將其發出。

默認情況下,我們會針對低延遲進行優化,但在流量非常高的情況下,或者當 master 和 replicas 距離多個 hops 時,將此選項改爲“yes”可能會更好。

repl-backlog-size

設置複製的 backlog 緩衝大小,默認 1mb。backlog 是一個緩衝區,當 replica 斷開一段時間連接時,它會累積 replica 數據,所以當 replica 想要再次重新連接時,一般不需要全量同步,只需要進行部分同步即可,只傳遞 replica 在斷開連接時丟失的部分數據。

更大的 backlog 緩衝大小,意味着 replicas 斷開重連後,依然可以進行續傳的時間越長(支持斷開更長時間)。

backlog 緩衝只有在至少一個 replica 節點連過來的時候 master 節點才需要創建。

repl-backlog-ttl

當 replicas 節點斷開連接後,master 節點會在一段時間後釋放 backlog 緩衝區。 這個選項設置的是當最後一個 replica 斷開鏈接後,master 需要等待多少秒再釋放緩衝區。默認3600 秒,0表示永遠不釋放。

replicas 節點永遠都不會釋放這個緩衝區,因爲它有可能再次連接到 master 節點, 然後嘗試進行 “增量同步”。

replica-priority

replica-priority 是 Redis 通過 INFO 接口發佈的整數,默認值爲 100。 當 master 節點無法正常工作後 Redis Sentinel 通過這個值來決定將哪個 replica 節點提升爲 master 節點。這個數值越小表示越優先進行提升。如有三個 replica 節點其 priority 值分別爲 10,100,25, Sentinel 會選擇 priority 爲 10 的節點進行提升。這個值爲 0 表示 replica 節點永遠不能被提升爲 master 節點。

min-replicas-to-write

min-replicas-max-lag

//表示要求至少3個延遲<=10秒的副本存在
min-replicas-to-write 3  //下文中的 N
min-replicas-max-lag 10 //下文中的 M

從 Redis 2.8 開始,如果連接的 replica 延遲小於或等於M秒的個數少於N個(N個 replica 需要處於“online”狀態),則 master 可能停止接受寫入並回復 error。由於 Redis 使用異步複製,因此無法確保 replica 是否實際接收到給定的寫命令,因此總會有一個數據丟失窗口。

原理如下:

  • replica 每秒鐘都會 ping master,確認已處理的複製流的數量;
  • master 會記得上一次從每個 replica 都收到 ping 的時間,延遲就是根據 master 從 replica 接收的最後一次 ping 計算的;
  • 用戶可以配置延遲不超過最大秒數的最小 replica 數;

此選項不保證 N 個副本將接受寫入,但在沒有足夠的副本可用的情況下,將丟失寫入的暴露窗口限制在指定的秒數內。

N 默認值爲 0,M 默認值爲10。任意一個設置爲 0 表示不啓用此功能。

replica-announce-ip 5.5.5.5

replica-announce-port 1234

Redis master 可以通過不同方式列出連接上來的 replicas 節點的地址和端口。 如 Redis Sentinel 等會使用 “INFO replication” 命令來獲取 replica 實例信息,master 的“ROLE“ 命令也會提供此信息。

這個信息一般來說是通過 replica 節點通過以下方式獲取然後報告上來的:

  • IP:通過自動識別連接到 Socket 的信息自動獲取
  • Port:一般來說這個值就是 replicas 節點用來接受客戶端的連接的監聽端口

但是,若啓用了端口轉發或者 NAT,可能需要其他地址和端口才能連接到 replicas 節點。 這種情況下,需要設置這兩個選項,這樣 replicas 就會用這兩個選項設置的值覆蓋默認行爲獲取的值,然後報告給 master 節點。 根據實際情況,你可以只設置其中某個選項,而不用兩個選項都設置。

配置的介紹到這裏就結束了,接下來我們把上邊提到的概念串起來,聊一下主從複製的相關原理。

原理

系統的運行依靠三個主要的機制

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

Redis 複製功能是如何工作的

每一個 Redis master 都有一個 replication ID :這是一個較大的僞隨機字符串,標記了一個給定的數據集。每個 master 也持有一個偏移量,master 將自己產生的複製流發送給 replica 時,發送多少個字節的數據,自身的偏移量就會增加多少,目的是當有新的操作修改自己的數據集時,它可以以此更新 replica 的狀態。

複製偏移量即使在沒有一個 replica 連接到 master 時,也會自增,所以基本上每一對給定的 Replication ID, offset 都會標識一個 master 數據集的確切版本。

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

說到這兒,那什麼是全量同步,那什麼又是增量同步呢?

全量同步

  1. replica 連接 master,發送 PSYNC 命令;
  2. master 執行 bgsave 開啓一個後臺保存進程,以便於生產一個 RDB 文件。同時它開始緩衝所有從客戶端接收到的新的寫入命令。
  3. 當後臺保存完成時, master 將數據集文件傳輸給所有的 replica,並在發送期間繼續記錄被執行的寫命令;
  4. replica 收到 RDB 文件之後,丟棄所有的舊數據,然後加載新文件到內存;
  5. replica 加載完成後,通知 master 發送所有緩衝的命令給 replica,這個過程以指令流的形式完成並且和 Redis 協議本身的格式相同;
  6. replica 開始接收命令請求,並執行來自 master 緩衝區的寫命令。

注意:SYNC 是一箇舊協議,在新的 Redis 中已經不再被使用,但是仍然向後兼容。因爲它不允許部分重同步,所以現在 PSYNC 被用來替代 SYNC。

正常情況下,一個全量重同步要求在磁盤上創建一個 RDB 文件,然後將它從磁盤加載進內存,然後 replica 以此進行數據同步。如果磁盤性能很低的話,這對 master 是一個壓力很大的操作。Redis 2.8.18 是第一個支持無磁盤複製的版本。在此設置中,子進程直接發送 RDB 文件給 replica 的 sockets 中,無需使用磁盤作爲中間儲存介質。

增量同步

master 把命令發送給所有的 replica 的同時,還會將命令寫入 backlog 緩衝區裏面。

當 replica 與 master 斷開連接又重新連接之後,此時要判斷 replica 的偏移量與 master 的偏移量的差集有沒有超過 backlog 的大小,

  • 如果沒有則給 replica 發送 CONTINUE,等待 master 將 backlog 中的數據發送給 replica;
  • 如果超過了則返回 FULLRESYNC runid offset,replica 將 runid 保存起來,並進行全量同步;

最後我們來聊幾個在面試過程中經常提到的面試題。

面試題

在主從複製過程中,關閉 master 的持久化會引發什麼問題呢?

數據會從 master 和所有 replica 中被刪除。我們用案例來說明一下:

  1. 我們設置節點 A 爲 master 並關閉它的持久化設置,設置節點 B 和 C 爲 replica;
  2. 當 master 崩潰時,由於系統中配置了自動重啓的腳本,此時 master 會自動重啓。但是由於持久化被關閉了,master 重啓後其數據集合爲空;
  3. 此時,如果 replica 從 master 中同步數據,就會導致 replica 中的數據也會變爲空集合。

因此,我們在使用 Redis 複製功能時,強烈建議在 master 和 replica 中啓用持久化。如果因爲非常慢的磁盤性能導致的延遲問題而不啓用持久化時,應該配置節點來避免重置後自動重啓

Redis 複製如何處理 key 的過期問題

Redis 的過期機制可以限制 key 的生存時間,該機制取決於 Redis 計算時間的能力。但是,即使使用 Lua 腳本將這些 key 變爲過期的 key,Redis replicas 也能正確地複製這些 key。

爲了實現這樣的功能,Redis 不能依靠主從使用同步時鐘,因爲這是一個無法解決的並且會導致 race condition 和數據集不一致的問題,所以 Redis 使用三種主要的技術使過期的 key 的複製能夠正確工作:

  • replica 不會讓 key 過期,而是等待 master 讓 key 過期。當一個 master 讓一個 key 到期(或由於 LRU 算法將之驅逐)時,它會合成一個 DEL 命令並傳輸到所有的 replica;
  • 由於主驅動的原因,master 無法及時提供 DEL 命令,所以有時候 replica 的內存中仍然可能存在邏輯上已經過期的 key。爲了處理這個問題,replica 使用它的邏輯時鐘來報告在不違反數據一致性的前提下,讀取操作的 key 不存在。用這種方法,replica 避免報告邏輯過期的 key 仍然存在。在實際應用中,使用 replica 程序進行擴展的 HTML 碎片緩存,將避免返回已經比期望的時間更早的數據項。
  • 在Lua腳本執行期間,不執行任何 key 過期操作。當一個Lua腳本運行時,從概念上講,master 中的時間是被凍結的,這樣腳本運行的時候,一個給定的鍵要麼存在要麼不存在。這可以防止 key 在腳本中間過期,保證將相同的腳本發送到 replica ,從而在二者的數據集中產生相同的效果。

一旦一個 replica 被提升爲一個 master ,它將開始獨立地過期 key,而不需要任何舊 master 的幫助。

以上就是今天的全部內容了,如果你有不同的意見或者更好的idea,歡迎聯繫阿Q,添加阿Q可以加入技術交流羣參與討論呦!

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