單機的 redis,能夠承載的 QPS 大概就在上萬到幾萬不等。對于緩存來說,一般都是用來支撐讀高并發的。因此架構做成主從(master-slave)架構,一主多從,主負責寫,并且將數據復制到其它的 slave 節點,從節點負責讀。所有的讀請求全部走從節點。這樣也可以很輕松實現水平擴容,支撐讀高并發。
redis replication -> 主從架構 -> 讀寫分離 -> 水平擴容支撐讀高并發
redis replication 的核心機制
- redis 采用異步方式復制數據到 slave 節點,不過 redis2.8 開始,slave node 會周期性地確認自己每次復制的數據量;
- 一個 master node 是可以配置多個 slave node 的;
- slave node 也可以連接其他的 slave node;
- slave node 做復制的時候,不會 block master node 的正常工作;
- slave node 在做復制的時候,也不會 block 對自己的查詢操作,它會用舊的數據集來提供服務;但是復制完成的時候,需要刪除舊數據集,加載新數據集,這個時候就會暫停對外服務了;
- slave node 主要用來進行橫向擴容,做讀寫分離,擴容的 slave node 可以提高讀的吞吐量。
注意,如果采用了主從架構,那么建議必須開啟 master node 的持久化,不建議用 slave node 作為 master node 的數據熱備,因為那樣的話,如果你關掉 master 的持久化,可能在 master 宕機重啟的時候數據是空的,然后可能一經過復制, slave node 的數據也丟了。
另外,master 的各種備份方案,也需要做。萬一本地的所有文件丟失了,從備份中挑選一份 rdb 去恢復 master,這樣才能確保啟動的時候,是有數據的,即使采用了后續講解的高可用機制,slave node 可以自動接管 master node,但也可能 sentinel 還沒檢測到 master failure,master node 就自動重啟了,還是可能導致上面所有的 slave node 數據被清空。
redis 主從復制的核心原理
當啟動一個 slave node 的時候,它會發送一個 PSYNC 命令給 master node。
如果這是 slave node 初次連接到 master node,那么會觸發一次 full resynchronization 全量復制。此時 master 會啟動一個后臺線程,開始生成一份 RDB 快照文件,同時還會將從客戶端 client 新收到的所有寫命令緩存在內存中。RDB 文件生成完畢后, master 會將這個 RDB 發送給 slave,slave 會先寫入本地磁盤,然后再從本地磁盤加載到內存中,接著 master 會將內存中緩存的寫命令發送到 slave,slave 也會同步這些數據。slave node 如果跟 master node 有網絡故障,斷開了連接,會自動重連,連接之后 master node 僅會復制給 slave 部分缺少的數據。
主從復制的斷點續傳
從 redis2.8 開始,就支持主從復制的斷點續傳,如果主從復制過程中,網絡連接斷掉了,那么可以接著上次復制的地方,繼續復制下去,而不是從頭開始復制一份。
master node 會在內存中維護一個 backlog,master 和 slave 都會保存一個 replica offset 還有一個 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 網絡連接斷掉了,slave 會讓 master 從上次 replica offset 開始繼續復制,如果沒有找到對應的 offset,那么就會執行一次 resynchronization。
如果根據 host+ip 定位 master node,是不靠譜的,如果 master node 重啟或者數據出現了變化,那么 slave node 應該根據不同的 run id 區分。
無磁盤化復制
master 在內存中直接創建 RDB,然后發送給 slave,不會在自己本地落地磁盤了。只需要在配置文件中開啟 repl-diskless-sync yes 即可。
repl-diskless-sync yes # 等待 5s 后再開始復制,因為要等更多 slave 重新連接過來 repl-diskless-sync-delay 5
過期 key 處理
slave 不會過期 key,只會等待 master 過期 key。如果 master 過期了一個 key,或者通過 LRU 淘汰了一個 key,那么會模擬一條 del 命令發送給 slave。
復制的完整流程
slave node 啟動時,會在自己本地保存 master node 的信息,包括 master node 的host和ip,但是復制流程沒開始。
slave node 內部有個定時任務,每秒檢查是否有新的 master node 要連接和復制,如果發現,就跟 master node 建立 socket 網絡連接。然后 slave node 發送 ping 命令給 master node。如果 master 設置了 requirepass,那么 slave node 必須發送 masterauth 的口令過去進行認證。master node 第一次執行全量復制,將所有數據發給 slave node。而在后續,master node 持續將寫命令,異步復制給 slave node。
全量復制
- master 執行 bgsave ,在本地生成一份 rdb 快照文件。
- master node 將 rdb 快照文件發送給 slave node,如果 rdb 復制時間超過 60秒(repl-timeout),那么 slave node 就會認為復制失敗,可以適當調大這個參數(對于千兆網卡的機器,一般每秒傳輸 100MB,6G 文件,很可能超過 60s)
- master node 在生成 rdb 時,會將所有新的寫命令緩存在內存中,在 slave node 保存了 rdb 之后,再將新的寫命令復制給 slave node。
- 如果在復制期間,內存緩沖區持續消耗超過 64MB,或者一次性超過 256MB,那么停止復制,復制失敗。
client-output-buffer-limit slave 256MB 64MB 60
- slave node 接收到 rdb 之后,清空自己的舊數據,然后重新加載 rdb 到自己的內存中,同時基于舊的數據版本對外提供服務。
- 如果 slave node 開啟了 AOF,那么會立即執行 BGREWRITEAOF,重寫 AOF。
增量復制
- 如果全量復制過程中,master-slave 網絡連接斷掉,那么 slave 重新連接 master 時,會觸發增量復制。
- master 直接從自己的 backlog 中獲取部分丟失的數據,發送給 slave node,默認 backlog 就是 1MB。
- master 就是根據 slave 發送的 psync 中的 offset 來從 backlog 中獲取數據的。
heartbeat
主從節點互相都會發送 heartbeat 信息。
master 默認每隔 10秒 發送一次 heartbeat,slave node 每隔 1秒 發送一個 heartbeat。
異步復制
master 每次接收到寫命令之后,先在內部寫入數據,然后異步發送給 slave node。
redis 如何才能做到高可用
如果系統在 365 天內,有 99.99% 的時間,都是可以嘩嘩對外提供服務的,那么就說系統是高可用的。
一個 slave 掛掉了,是不會影響可用性的,還有其它的 slave 在提供相同數據下的相同的對外的查詢服務。
但是,如果 master node 死掉了,會怎么樣?沒法寫數據了,寫緩存的時候,全部失效了。slave node 還有什么用呢,沒有 master 給它們復制數據了,系統相當于不可用了。
redis 的高可用架構,叫做 failover 故障轉移,也可以叫做主備切換。
master node 在故障時,自動檢測,并且將某個 slave node 自動切換為 master node 的過程,叫做主備切換。這個過程,實現了 redis 的主從架構下的高可用。
后面會詳細說明 redis 基于哨兵的高可用性。