今天来聊聊 Redis 的主从复制
作者 | 阿Q
來源 | 阿Q說代碼
今天我們就從配置文件、設計原理、面試真題三個方面來聊一聊 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,傳輸可以通過兩種不同的方式進行:
Disk-backed:Redis master 節點創建一個新的進程并將 RDB 文件寫入磁盤,然后文件通過父進程增量傳輸給 replicas 節點;
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 會得到一個完整的數據集副本,從頭開始。
說到這兒,那什么是全量同步,那什么又是增量同步呢?
全量同步
replica 連接 master,發送 PSYNC 命令;
master 執行 bgsave 開啟一個后臺保存進程,以便于生產一個 RDB 文件。同時它開始緩沖所有從客戶端接收到的新的寫入命令。
當后臺保存完成時, master 將數據集文件傳輸給所有的 replica,并在發送期間繼續記錄被執行的寫命令;
replica 收到 RDB 文件之后,丟棄所有的舊數據,然后加載新文件到內存;
replica 加載完成后,通知 master 發送所有緩沖的命令給 replica,這個過程以指令流的形式完成并且和 Redis 協議本身的格式相同;
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 保存起來,并進行全量同步;
往期推薦
虛幻引擎5上的《黑客帝國》全新體驗,愛了愛了
Medusa又一個開源的替代品
Log4j 第三次發布漏洞補丁,漏洞或將長存
5G專網,路在何方?
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的今天来聊聊 Redis 的主从复制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 火山引擎进军云市场,计划未来三年服务十万
- 下一篇: 随时随地办公新常态 还需安全来相伴