Redis的高可靠性主要包括兩方面:
數據儘量少丟失:RDB & AOF機制
服務儘量少中斷:增加副本冗餘
主從模式Redis提供了主從庫模式,增加冗餘的副本來提高Redis集群的高可靠性。主從庫之間採用讀寫分離的方式,寫請求只能在主庫,讀請求在主從庫都可以完成。
讀操作:主庫、從庫
寫操作:主庫 --> 主庫寫完後同步從庫
寫請求為什麼只能在主庫上,若從庫和主庫上都可以進行讀寫會發生什麼?
主從庫之間是如何同步的全量同步:當從庫是第一次建立連接並開始同步,該種情況下的同步如下圖:
從庫和主庫建立起連接,並告訴主庫即將進行同步,主庫確認回復後,主從庫間就可以開始同步了。首先,從庫給主庫發送 psync 命令,表示要進行數據同步,主庫根據這個命令的參數來啟動複製。psync 命令包含了主庫的 runID 和複製進度 offset 兩個參數。
(1)runID,是每個 Redis 實例啟動時都會自動生成的一個隨機 ID,用來唯一標記這個實例。當從庫和主庫第一次複製時,因為不知道主庫的 runID,所以將 runID 設為「?」。
(2)offset,此時設為 -1,表示第一次複製。
FULLRESYNC 響應表示第一次複製採用的全量複製,也就是說,主庫會把當前所有的數據都複製給從庫。在第二階段,主庫將所有數據同步給從庫。從庫收到數據後,在本地完成數據加載。這個過程依賴於內存快照生成的 RDB 文件。為了保證主從庫的數據一致性,主庫會在內存中用專門的 replication buffer,記錄 RDB 文件生成後收到的所有寫操作。
主庫會把第二階段執行過程中新收到的寫命令,再發送給從庫。具體的操作是,當主庫完成 RDB 文件發送後,就會把此時 replication buffer 中的修改操作發給從庫,從庫再重新執行這些操作。這樣一來,主從庫就實現同步了。
增量同步:
Redis增量複製是指replica初始化後開始正常工作時主伺服器發生的寫操作同步到從伺服器的過程。增量複製的過程主要是主伺服器每執行一個寫命令就會向從伺服器發送相同的寫命令,從伺服器接收並執行收到的寫命令。
Redis的主節點創建和維護一個環形緩衝複製隊列(即repl_backlog_buffer),從節點部分複製(增量複製)的數據均來自於repl_backlog_buffer。主節點只有一個repl_backlog_buffer,所有從節點共享。
主-從-從模式減少主庫全量同步時的壓力主從庫模式中,所有的從庫都是和主庫連接,所有的全量複製也都是和主庫進行的。現在,我們可以通過「主 - 從 - 從」模式將主庫生成 RDB 和傳輸 RDB 的壓力,以級聯的方式分散到從庫上。如下圖:
主從庫網絡中斷怎麼辦Redis的主節點創建和維護一個環形緩衝複製隊列(即repl_backlog_buffer),從節點部分複製(增量複製)的數據均來自於repl_backlog_buffer。主節點只有一個repl_backlog_buffer,所有從節點共享。
當主從庫斷連後,主庫每次都會把收到的寫操作命令,寫入 replication buffer,同時也會把這些操作命令也寫入 repl_backlog_buffer 這個緩衝區。主庫會記錄自己寫到的位置,從庫則會記錄自己已經讀到的位置。主庫來的偏移量是master_repl_offset,對從庫來說從庫已複製的偏移量是slave_repl_offset,正常情況下,這兩個偏移量基本相等。
主從庫的連接恢復之後,從庫首先會給主庫發送 psync 命令,並把自己當前的 slave_repl_offset 發給主庫,主庫會判斷自己的 master_repl_offset 和 slave_repl_offset 之間的差距。在網絡斷連階段,主庫可能會收到新的寫操作命令,所以,一般來說,master_repl_offset 會大於 slave_repl_offset。此時,主庫只用把 master_repl_offset 和 slave_repl_offset 之間的命令操作同步給從庫就行。
哨兵哨兵其實就是一個運行在特殊模式下的 Redis 進程,主從庫實例運行的同時,它也在運行。哨兵主要負責的就是三個任務:監控、選主(選擇主庫)和通知,哨兵節點其實就是一個特殊的Redis實例節點。
監控:判斷主從庫下線
選主:選出新主庫
通知:讓從庫與主庫同步,通知客戶端與主庫連接
哨兵與沒一個主庫和從庫相連接,並且哨兵節點之間行成集群。
監控哨兵與沒一個Redis節點相連接並通過PING命令檢測它自己和主、從庫的網絡連接情況,用來判斷實例的狀態。沒一個Redis節點的狀態可以分為」主觀下線「和」客觀下線「。
如果檢測的是從庫,那麼,哨兵簡單地把它標記為「主觀下線」就行了,因為從庫的下線影響一般不太大,集群的對外服務不會間斷。
如果檢測的是主庫,那麼,哨兵還不能簡單地把它標記為「主觀下線」,開啟主從切換。因為很有可能存在這麼一個情況:那就是哨兵誤判了,其實主庫並沒有故障。可是,一旦啟動了主從切換,後續的選主和通知操作都會帶來額外的計算和通信開銷。
在判斷主庫是否下線時,不能由一個哨兵說了算,只有大多數的哨兵實例,都判斷主庫已經「主觀下線」了,主庫才會被標記為「客觀下線」,這個叫法也是表明主庫下線成為一個客觀事實了。這個判斷原則就是:少數服從多數。同時,這會進一步觸發哨兵開始主從切換流程。
選主當主庫掛了以後,哨兵會在從庫中進行選主,選主主要包括以下三個原則。
首先,優先級最高的從庫得分高。
其次,和舊主庫同步程度最接近的從庫得分高。
最後,ID 號小的從庫得分高。
用戶可以通過 slave-priority 配置項,給不同的從庫設置不同優先級。比如,你有兩個從庫,它們的內存大小不一樣,你可以手動給內存大的實例設置一個高優先級。在選主時,哨兵會給優先級高的從庫打高分,如果有一個從庫優先級最高,那麼它就是新主庫了。如果從庫的優先級都一樣,那麼哨兵開始進行第二個原則判斷。
這個規則的依據是,如果選擇和舊主庫同步最接近的那個從庫作為主庫,那麼,這個新主庫上就有最新的數據。主從庫同步時有個命令傳播的過程。在這個過程中,主庫會用 master_repl_offset 記錄當前的最新寫操作在 repl_backlog_buffer 中的位置,而從庫會用 slave_repl_offset 這個值記錄當前的複製進度。
每個實例都會有一個 ID,這個 ID 就類似於這裡的從庫的編號。目前,Redis 在選主庫時,有一個默認的規定:在優先級和複製進度都相同的情況下,ID 號最小的從庫得分最高,會被選為新主庫。
分片集群當數據容量越來越大時,如Redis集群的數據量有5G增長到了25G,那麼需要考慮集群的擴容問題,在擴容時一般有兩種方案:
縱向擴展:升級單個 Redis 實例的資源配置,包括增加內存容量、增加磁碟容量、使用更高配置的 CPU。將磁碟容量擴展到50G
簡單直接,但是增加了硬體和成本的限制、當使用 RDB 對數據進行持久化時,如果數據量增加,需要的內存也會增加,主線程 fork 子進程時就可能會阻塞
橫向擴展:橫向增加當前 Redis 實例的個數,就像下圖中,原來使用 1 個 *GB 內存、10GB 磁碟的實例,現在使用三個相同配置的實例。
分片和實例的對應分布關係在分片集群中,數據需要分布在不同實例上,那麼,數據和實例之間如何對應呢?這就和接下來我要講的 Redis Cluster 方案有關了。不過,我們要先弄明白切片集群和 Redis Cluster 的聯繫與區別。
Redis Cluster 方案採用哈希槽(Hash Slot,接下來我會直接稱之為 Slot),來處理數據和實例之間的映射關係。在 Redis Cluster 方案中,一個切片集群共有 16384 個哈希槽,這些哈希槽類似於數據分區,每個鍵值對都會根據它的 key,被映射到一個哈希槽中。
以只有五個槽為例,展示,數據、哈希槽、實例這三者的映射分布情況,如下圖:
客戶端如何定位數據客戶端和集群實例建立連接後,實例就會把哈希槽的分配信息發給客戶端。但是,在集群剛剛創建的時候,每個實例只知道自己被分配了哪些哈希槽,是不知道其他實例擁有的哈希槽信息的。那麼,客戶端為什麼可以在訪問任何一個實例時,都能獲得所有的哈希槽信息呢?這是因為,Redis 實例會把自己的哈希槽信息發給和它相連接的其它實例,來完成哈希槽分配信息的擴散。當實例之間相互連接後,每個實例就有所有哈希槽的映射關係了。客戶端收到哈希槽信息後,會把哈希槽信息緩存在本地。當客戶端請求鍵值對時,會先計算鍵所對應的哈希槽,然後就可以給相應的實例發送請求了。
實例和哈希槽的對應關係並不是一成不變的,最常見的變化有兩個:
Redis Cluster 方案提供了一種重定向機制,所謂的「重定向」,就是指,客戶端給一個實例發送數據讀寫操作時,這個實例上並沒有相應的數據,客戶端要再給一個新實例發送操作命令。
需要注意的是,在上圖中,當客戶端給實例 2 發送命令時,Slot 2 中的數據已經全部遷移到了實例 3。在實際應用時,如果 Slot 2 中的數據比較多,就可能會出現一種情況:客戶端向實例 2 發送請求,但此時,Slot 2 中的數據只有一部分遷移到了實例 3,還有部分數據沒有遷移。在這種遷移部分完成的情況下,客戶端就會收到一條 ASK 報錯信息。
這個結果中的 ASK 命令就表示,客戶端請求的鍵值對所在的哈希槽 13320,在實例3上,但是這個哈希槽正在遷移。此時,客戶端需要先給 實例3這個實例發送一個 ASKING 命令。這個命令的意思是,讓這個實例允許執行客戶端接下來發送的命令。然後,客戶端再向這個實例發送 GET 命令,以讀取數據。
在下圖中,Slot 2 正在從實例 2 往實例 3 遷移,key1 和 key2 已經遷移過去,key3 和 key4 還在實例 2。客戶端向實例 2 請求 key2 後,就會收到實例 2 返回的 ASK 命令。ASK 命令表示兩層含義:第一,表明 Slot 數據還在遷移中;第二,ASK 命令把客戶端所請求數據的最新實例地址返回給客戶端,此時,客戶端需要給實例 3 發送 ASKING 命令,然後再發送操作命令。ASK 命令並不會更新客戶端緩存的哈希槽分配信息。
作者:買個橘子
連結:https://juejin.im/post/6894048481422344199