公司 Redis 宕機,老板怒了!
最近跟一位讀者聊天,小哥非常郁悶,公司的 Redis 宕機了,線上業務受到了影響,老板非常憤怒,小哥擔心會不會被辭退!
圖片來自 包圖網
我也很好奇,問小哥 Redis 主節點掛了,還有備機啊。怎么會影響到業務呢?小哥說,他們的系統架構只部署一個 Redis 單實例。節點掛了,數據也丟了。
好吧,既然提到了備份,那今天,我們就來聊下 Redis 的主從同步。
首先,什么是主從?主從也稱主從集群,部署了多個 Redis 實例,如下圖所示:
其中,每個實例又有自己的專屬職責:
- 主庫:負責接收讀操作、寫操作
- 從庫:定期同步主庫的數據,對外提供讀操作
好奇的寶寶可能要問了,為什么從庫不能寫?考慮到數據合并的復雜性,假如一個 key,多次更新,每次操作在不同的實例上執行,為了保證數據的全局一致性,勢必要加全局鎖。
保證在集群范圍上串行化操作且在最新的數據基礎上更新,這個成本還是很大的。
為了降低系統復雜度,節約成本。主從同步架構方案一般都是在主庫上寫,在從庫上讀。分工明確,職責單一。
可能有同學會提到 Redis Cluster 模式,這個是另一種設計方案。采用水平分割方式,通過 CRC16(key)算法,將數據拆分到若干個實例中。
每個實例只對自己負責的槽位的數據讀、寫,從而分攤集群壓力。這個屬于另一種玩法,本期就不深入展開了。
為了保證數據不丟失,Redis 提供兩種數據同步方式:
- RDB,全量數據同步
- AOF,增量數據同步,回放日志
這兩者有什么區別?什么時候采用 RDB ? 什么時候采用 AOF ?接下來,我們逐步分析展開。
建立主從關系
首先,啟動兩個 redis 實例,IP 地址分別是 192.168.0.1 和 192.168.0.2 ,開始時,他們之間沒有任何關聯。
我們通過終端命令,登錄 192.168.0.2 機器,執行命令:
- replicaof 192.168.0.1 6379
此時 192.168.0.2 實例就成了 192.168.0.1 的從庫。
當主從實例建立好關聯后,接下來,就開始進入數據同步環節。
主從同步
主從庫數據同步分為三步:
第一步:從庫(192.1768.0.2)向主庫(192.168.0.1)發送 psync 命令,帶了兩個參數(主庫的 runID 和同步進度 offset)。
第一次建立連接時,從庫并不知道主庫的 runID,所以會設置為?。offset = -1,表示第一次復制。
說明:每個 Redis 實例初始啟動時,會自動生成一個隨機 ID,用來標識當前實例。
主庫接收到 psync 請求后,會響應 FULLRESYNC ,帶有兩個參數(主庫的 runID 和同步進度 offset)。說明:FULLRESYNC 表示采用全量復制。
第二步:主庫 fork 子進程,執行 bgsave 命令,生成 RDB 文件;主庫將 RDB 文件發給從庫;從庫接到響應后,會先清空當前數據庫,然后加載 RDB 文件。
說明:主庫在生成 RDB 文件時,主線程是阻塞的,對外不提供服務。一旦 RDB 文件生成,在數據同步過程中,不受影響,主庫可以對外服務。后續的寫命令數據會存到 replication buffer。
第三步:主庫將增量寫命令發送給從庫,從庫放映式執行這些命令,從而實現了主從同步。
到這里,主從的核心邏輯基本講完了。但生產環境,通常是一主多從,每個從庫初始同步時,都要主庫生成 RDB 文件,顯然開銷很大。有什么解決方案?
一主多從,主庫減壓
當從節點存在多個時,主庫的壓力顯著增加,具體體現在兩個方面:
- 當從庫同步主庫時,要 fork 子進程,有多少個從節點,就要 fork 多少個子進程,每個子進程都要生成 RDB。導致主庫系統壓力過大。
- 生成的 RDB 要同步給從庫,占用網絡帶寬。
基于上面的困境,演化出新的模式,“主--從--從”模式,具體玩法如下圖:
現有雖然有四個從庫,但直接跟主庫關聯同步數據的只有 192.168.0.2 和 192.168.0.3 兩個實例,大大減輕了主庫的壓力。
任何事情都不是一成不變的,網絡傳輸就存在很大的風險,網絡閃斷了怎么辦?對主從同步有什么影響?
網絡閃斷對主從同步的影響
我們知道主從實例間同步數據主要有兩種方式:全量同步和增量同步。全量同步就是同步 RDB 文件,那增量同步是如何實現的呢?
這里要引入一個緩沖區,repl_backlog_buffer,它是一個環形設計,增量命令都是先存入這個緩沖區的。
主庫有生產位移,稱之為master_repl_offset 。從庫有拉取位移,稱之為slave_repl_offset。
正常情況下,master_repl_offset 和 slave_repl_offset 大小是接近的,也就是說主從庫兩者間的數據近乎同步。
每次同步數據時,從庫向主庫發送 psync 命令,把自己的 slave_repl_offset 發給主庫,主庫基于此偏移位置,向從庫發送增量數據。這個很容易理解。
是不是就萬無一失了呢?由于采用了環形結構,如果主庫的生產速度比從庫的拉取速度快很多時,就會出現套圈現象。
為什么采用環形?主要為了讓空間循環使用,像市場的行車記錄儀、監控設備等,大多都是采用循環覆蓋式存儲。
如果空間滿了,將之前最老的數據覆蓋掉。雖然可能丟失了部分數據,但是性價比高。
回到上面的問題,如果被套圈了怎么辦?
如上圖所示,從庫 psync 命令,請求的 offset 是 4,但是主節點已經生產到了 15 ,將之前的 1、2、3、4、5 全部覆蓋掉了。
這下傻眼了,需要同步的數據被覆蓋了,惹大麻煩了....
有兩個解決方案:
①調大 repl_backlog_buffer 緩沖區大小,該值是由 repl_backlog_size 參數控制
緩沖空間大小 = 主庫寫入速度 * 操作大小 - 從庫拉取速度 * 操作大小。
這是我們能主觀控制的。比如擔心大促帶來的流量高峰,可以將這個值調大 2 倍、3 倍、4 倍,大家可以根據自己的業務情況自由設置。
②還有一種方式是 Redis 自身提供的解決方案
此時會觸發全量復制,跟第一次建立主從關系同步數據一樣。通過全量方式,一次性彌補主從間的數據大缺口。
主節點掛了怎么辦
如果只是傳統意義上的主從模式,主節點掛了,通常要手工完成切換。效率不言而喻了,尤其是線上生產系統,根本沒法接受這種方案。
這時候,要引入哨兵機制了,哨兵機制可以實現主從庫的自動切換,有效解決了故障轉移。
整個過程分為三個階段:
- 監控
- 選主
- 通知
監控:哨兵進程會周期給所有的主庫、從庫發送 PING 命令,檢測機器是否處于服務狀態。如果沒有在設置時間內收到回復,則判定為下線。
當然,網絡抖動,也會存在誤判可能,如何避免?引入哨兵集群,多個哨兵實例一起判斷,降低誤判率。判斷標準就是,假如 n 個哨兵實例,至少有 n/2+1 個判定一致,才可以定論。
選主:主要是看各個節點的打分情況,打分規則分為從庫優先級、從庫復制進度、從庫 ID 號。
只要有一輪,某個從庫得分最高,則選舉它為主庫:
- 從庫優先級,主要是考慮到不同的機器可能配置不一樣,配置高的機器,優先級高一些,通過 slave-priority 來配置。
- 從庫復制進度,主要是看 slave_repl_offset 的值大小,值越大表示已經同步的數據越多,得分越高。
- 從庫 ID 號,每個 Redis 實例啟動時,都會生成一個 ID,在優先級和復制進度相同的條件下,ID 號最小的從庫分數最高,會被選為新主庫。
通知:把選舉后的新主庫發送給所有節點,讓所有的從庫執行 replicaof 命令,和 master 建立主從關系、數據同步復制。另外,也會把最新的主庫信息同步給客戶端。
作者:Tom 哥,前阿里 P7 技術專家
編輯:陶家龍
來源:轉載自公眾號微觀技術(ID:weiguanjishu)