Redis 高可用的方案包括:持久化、主從複製(及讀寫分離)、哨兵和集羣(Cluster)。
持久化
解決Redis 數據的單機備份問題(從內存到硬盤的備份),數據可用性以及可靠性。
主從複製
解決數據的多機熱備,以及從實例角度進行解決提高了高可用(故障切換),主從複製還可以實現負載均衡(高性能)。
哨兵
自動進行監控和切換,進行解決實例之間提高了高可用(故障切換)
集羣
進行數據容災以及高可用的能力,此外可以擴展單節點 redis 的數據存儲上限。
一:redis 持久化
Redis 數據恢復的背景
redis 的數據一般全部存儲在內存中,這種情況下數據庫一旦故障發生重啓數據會全部丟失。
持久化功能在於能夠有效地避免因進程退出造成的數據丟失問題,在下次重啓時利用之前持久化的文件即可實現數據恢復。
Redis 因爲在內存中進行數據存儲和操作;如果僅僅是在內存中進行數據存儲,那就會導致以下問題:
-
數據隨進程退出而消失:當服務器斷電或 Redis Server 進程退出時,內存肯定隨之釋放,最後數據也會丟失;
- 如果只是作爲緩存,數據沒有了則重新從數據庫中讀取放在裏面即可,那麼如果是高併發場景,數據庫壓力依然很大;
-
數據丟失之後無法進行恢復:數據只存在 Redis 中,而沒有存在關係型數據庫,如果數據丟失便不可恢復;
Redis 持久化有兩種方案
-
RDB(Redis DataBase)是一種快照式的二進制數據存儲,它會週期性的保存當前時間點 Redis 所有的數據到磁盤中。
-
AOF(Append Of FIle)是一種追加式的存儲方式,會實時的記錄 Redis 的寫操作到磁盤中。
Redis 持久化機制實現
- Redis 是基於內存進行操作運算,如果不持久化數據再重啓服務時會導致數據丟失。
- 開啓 Redis 持久化功能後,數據會保存到磁盤中。當 redis 重啓後,可以從磁盤中恢復數據。
RDB 快照(snapshot)
RDB 持久化方式
RDB 持久化把當前進程數據生成快照(.rdb)文件保存到硬盤的過程,有手動觸發和自動觸發。
RDB 手動觸發
手動觸發有 save 和 bgsave 兩命令。
save 命令
-
該命令會阻塞當前 Redis 服務器,執行 save 命令期間,Redis 不能處理其他命令,直到 RDB 過程完成爲止。
-
執行完成時候如果存在老的 RDB 文件,就把新的替代掉舊的。我們的客戶端可能都是幾萬或者是幾十萬,這種方式顯然不可取。
在執行 redis-cli shutdown 關閉 redis 服務時,如果沒有開啓 AOF 持久化,自動執行 save。
bgsave 命令
-
Redis 主進程 fork 一個子進程來創建臨時 RDB 存儲文件,創建文件完成後對這個臨時文件 rename 替換原先的 RDB 文件。
-
RDB 文件是一個單文件很適合數據的容災備份與恢復,通過 RDB 文件恢復數據庫耗時較短,通常 1G 的快照文件載入內存只需 20s 左右。
bgsave 命令和 save 命令
命令名稱 | save | bgsave |
---|---|---|
IO 類型 | 同步 | 異步 |
是否阻塞 | 是 | 否 |
複雜度 | O(n) | O(n) |
優點 | 不會耗費額外內存 | 不阻塞客戶端訪問 |
缺點 | 阻塞客戶端訪問 | 耗費多餘客戶端 |
在指定時間間隔內將內存中的數據庫記錄集 dump 到磁盤上,RDB 是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中 , 默認的文件名爲 dump.rdb。
RDB 自動觸發
自動觸發是由我們的配置文件來完成的,自動觸發 bgsave。
配置方法
-
在 redis.conf 文件中配置 N 秒內數據集至少有 M 個改動時自動觸發一次 RDB 持久化,Redis 會將數據集的快照 dump 到 dump.rdb 文件中。
-
我們也可以通過配置文件來修改 Redis 服務器 dump 快照的頻率,在打開 6379.conf 文件之後,我們搜索 save,可以看到下面的配置信息:
60 秒內至少有 10000 個鍵被修改
- save 900 1 #在 900 秒 (15 分鐘) 之後,如果至少有 1 個 key 發生變化,則 dump 內存快照。
- save 300 10 #在 300 秒 (5 分鐘) 之後,如果至少有 10 個 key 發生變化,則 dump 內存快照。
- save 60 10000 #在 60 秒 (1 分鐘) 之後,如果至少有 10000 個 key 發生變化,dump 內存快照。
關閉 RDB 方式持久化只需要將所有 save 保存策略註釋掉即可
RDB 持久化命令
- 命令:config set dir /usr/local // 設置 rdb 文件保存路徑
- 備份:bgsave // 將 dump.rdb 保存到 usr/local 下
- 恢復:將 dump.rdb 放到 redis 安裝目錄與 redis.conf 同級目錄,重啓 redis 即可
save 命令是同步命令,bgsave 命令是異步命令,會從 redis 主進程 fork 出一個子進程去處理,每次命令執行後會新生成一個 rdb 文件並覆蓋原來的文件。
修改配置
-
rdbcompression yes:rdb 文件壓縮是否開啓
-
dbfilename dump.rdb:rdb 文件名稱定義
RDB 的優缺點
優點
- 恢復速度快,適合大規模的數據恢復。RDB 保存的是某一個時間點的內存快照,非常適合災難恢復。在恢復大數據集時速度快,1G 的 RDB 數據恢復耗時大概 20s,比 AOF 要快的多
- 如果業務對數據完整性和一致性要求不高,RDB 是很好的選擇。
缺點
- 數據的完整性和一致性不高,間隔時間大並且丟失數據較爲多,RDB 通過觸發某個時間點條件生成快照文件,如果 5 分鐘保存一次的話,一旦發生故障會丟失好幾分鐘的數據,配置不同的保存點讓 RDB 至少可以保存 5 分鐘的數據。redis 因爲沒有正確關閉而停止工作,則丟失最近幾分鐘的數據
- 備份時,CPU 資源佔用過大、內存資源佔用過多。因爲 Redis 在備份時會獨立創建一個子進程,將數據寫入到一個臨時文件(此時內存中的數據是原來的兩倍),最後再將臨時文件替換之前的備份文件。
- fork 子進程消耗內存和 CPU,由於 RDB 是通過 fork 子進程來協助完成數據持久化工作的,如果數據集較大,可能會導致整個服務器停止服務幾百毫秒,甚至是 1 秒鐘。(回寫和覆蓋的時候用的是主進程)。
AOF(append-only file)
由於 RDB 快照方式的缺點,如果 redis 由於某些原因導致機器故障時,則會丟失最近幾分鐘寫入的數據,而 AOF 持久化方式,則通過追加的方式將操作命令添加到 appendonly.aof 文件中,存儲的文件是 RESP 協議指令文件。
配置方法
-
appendonly yes # 開啓 AOF 持久化,(默認不開啓,爲 no)
-
appendfilename “appendonly.aof”:默認文件名
配置好後,redis 每次修改操作的命令會追加到 AOF 末尾,當 redis 重啓後會重新執行 AOF 裏的命令達到重建緩存數據集的目的,配置刷命令的頻率。
-
appendfsync always :每次有新命令追加到 AOF 文件時就執行一次 fsync,非常慢但最安全。服務器在每執行一個事件就把 AOF 緩衝區的內容強制性的寫入硬盤上的 AOF 文件裏,保證了數據持久化的完整性,效率是最慢的但最安全的;
-
appendfsync everysec # 服務端每隔一秒纔會進行一次文件同步把內存緩衝區裏的 AOF 緩存數據真正寫入 AOF 文件裏,兼顧了效率和完整性,極端情況服務器宕機只會丟失一秒內對 Redis 數據庫的寫操作;默認方式
-
appendfsync no # 從不 fsync,交由操作系統處理,速度快,表示默認系統的緩存區寫入磁盤的機制,不做程序強制,數據安全性和完整性差一些。
-
bgrewriteaof:後臺運作重寫機制
-
auto-aof-rewrite-min-size 64mb # aof 文件至少要達到 64M 纔會自動重寫,文件太小恢復速度本來就很快,重寫的意義不大
-
auto-aof-rewrite-percentage 100 # aof 文件自上一次重寫後文件大小增長了 100%,則再次觸發重寫執行 bgrewriteaof 命令可以手動重寫 aof 文件,AOF 重寫 redis 會 fork 出一個子進程去做,不會對 redis 正常命令處理有太多影響。
redis 啓動時如果既有 rdb 文件又有 aof 文件則優先選擇 aof 文件恢復數據,因爲 aof 一般來說數據更全一點。
-
全量同步 (RDB):每天定時(避開高峯期)或者採用一個週期實現將數據拷貝到一個地方。
-
增量同步 (AOF):比如採用對行爲的操作實現對數據的同步。
-
增量同步比全量同步更加消耗服務器的內存,但是能夠更加的保證數據的同步。
AOF 重寫原理
AOF 的工作原理是將寫操作追加到文件中,文件的冗餘內容會越來越多。 Redis 新增了重寫機制。當 AOF 文件的大小超過所設定的閾值時,Redis 就會對 AOF 文件的內容壓縮。Redis 會 fork 出一條新進程,讀取內存中的數據,並重新寫到一個臨時文件中。並沒有讀取舊文件。最後替換舊的 aof 文件。AOF 文件重寫是把 Redis 進程內的數據轉化爲寫命令同步到新 AOF 文件的過程。
需要壓縮重寫的案例:
-
AOF 重寫,持久化文件會變得越來越大。比如,我們調用 INCR test 命令 100 次,文件中就必須保存全部的 100 條命令,但其實 99 條都是多餘的。
- 因爲要恢復數據庫的狀態其實文件中保存一條 SET test 100 就夠了。
-
Redis 提供了 bgrewriteaof 命令,來合併重寫 AOF 的持久化文件。收到命令後,Redis 將使用與快照類似的方式將內存中的數據以命令的方式保存到臨時文件中,最後替換原來的文件,以此來實現控制 AOF 文件的合併重寫(會將重寫過程中接收的的新的指令和生成新的重寫後 AOF 文件中的指令進行合併)。
注意:由於是模擬快照的過程,因此在重寫 AOF 文件時並沒有讀取舊的 AOF 文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的 AOF 文件
AOF 重寫目的
- AOF 重寫降低了文件佔用空間。
- 更小的 AOF 文件可以更快地被 Redis 加載。
AOF 重寫過程
可以手動觸發和自動觸發:
- 配置重寫 rewrite 觸發機制
- 手動觸發:直接調用 bgrewriteaof 命令
- 自動觸發:根據 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 參數確定自動觸發時機
- auto-aof-rewrite-min-size: 限制了允許重寫的最小 AOF 文件,通常在 AOF 文件很小的時候即使其中有些冗餘命令也可是可以忽略的。
- auto-aof-rewrite-percentage: 當前的 AOF 文件大小超過上一次重寫的 AOF 文件大小的百分之多少時會再次進行重寫,如果之前沒有重寫過,則以啓動時的 AOF 大小爲依據。
注意,執行 AOF 重寫請求時,父進程依然響應命令,Redis 使用 "AOF 重寫緩衝區" 保存這部分新數據,防止新 AOF 文件生成期間丟失這部分數據。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
解釋含義:當 AOF 文件大小是上次 rewrite 後大小的一倍且文件大於 64M 時觸發。一般都設置爲 3G,64M 太小了, 這裏的 “一倍” 和 “64M” 可以通過配置文件修改。
重寫後的 AOF 文件爲什麼可以變小?
有如下原因:
- 進程內已經超時的數據不再寫文件。
- 舊的 AOF 文件含有無效命令,如 del key1、set a 111、set a 222 等。重寫使用進程內數據直接生成,這樣新的 AOF 文件只保留最終數據的寫入命令。
- 多條寫命令可以合併爲一個,如 lpush list a、lpush list b、 lpush list c 可以轉化爲:lpush list a b c。
爲了防止合併的數據過大造成客戶端緩衝區溢出,對於 list、set、hash、zset 等類型,以 64 個元素爲界拆分爲多條。
AOF 的優缺點
優點
- 數據的完整性和一致性更高
- AOF 是通過保存 Redis 寫操作的命令來實現持久化,使用 AOF 來持久化,Redis 數據的安全性將大幅提高,異常宕機情況下最多丟失 1s 的數據。AOF 文件記錄了 redis 的寫操作,格式清晰,易於理解和修改,利於數據的重建。
- AOF 日誌是一個只附加的日誌,因此如果斷電,就不會出現查找或損壞問題。即使日誌由於某種原因(磁盤已滿或其他原因)以半寫的命令結束,redis check aof 工具也可以輕鬆地修復它。
- 當 AOF 太大時,Redis 能夠在後臺自動重寫 AOF。重寫是完全安全的,因爲當 Redis 繼續附加到舊文件時,一個全新的文件會生成,只需創建當前數據集所需的最少操作集,一旦第二個文件就緒,Redis 就會切換這兩個文件並開始附加到新文件中。
- AOF 以易於理解和解析的格式包含所有操作的日誌。我們甚至可以輕鬆導出 AOF 文件。
AOF 劣勢
-
同步策略的不同,AOF 在運行效率上往往會慢於 RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和 RDB 一樣高效。
-
數據的完整性和一致性更高因爲 AOF 記錄的內容多,文件會越來越大,數據恢復也會越來越慢。
-
對於相同數量的數據集而言,AOF 文件通常比相同數據集的等效 RDB 文件大。
-
根據具體的 fsync 策略,RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。
總結
每秒同步策略的效率是比較高的,同步禁用策略的效率和 RDB 一樣高效。
- AOF 的數據完整性比 RDB 高,但記錄內容多了,會影響數據恢復的效率。
- RDB 與 AOF 二者選擇的標準,就是看系統是願意犧牲一些性能,換取更高的緩存一致性(aof),還是願意寫操作頻繁的時候,不啓用備份來換取更高的性能,待手動運行 save 的時候,再做備份(rdb)。
- Redis 允許同時開啓 AOF 和 RDB,既保證了數據安全又使得進行備份等操作十分容易。此時重新啓動 Redis 後 Redis 會使用 AOF 文件來恢復數據,因爲 AOF 方式的持久化可能丟失的數據更少。
- 若只打算用 Redis 做緩存,可以關閉持久化。
使用建議
Redis 默認開啓了持久化功能,而且是全量 RDB 的,缺點是服務器宕機後可能會造成數據丟失。
建議最好還是搭配使用 aof 的 everysec,既能夠保證數據的同步,效率也還可以,但是會存在丟失一秒數據的可能性,就算丟失也關係不大,因爲數據庫中已經存在了數據。
使用總結
-
Redis 默認開啓 RDB 持久化方式,在指定的時間間隔內,執行指定次數的寫操作,則將內存中的數據寫入到磁盤中。
-
RDB 持久化適合大規模的數據恢復但它的數據一致性和完整性較差。
-
Redis 需要手動開啓 AOF 持久化方式,默認是每秒將寫操作日誌追加到 AOF 文件中。
所以 Redis 的持久化和數據的恢復要選擇在夜深人靜的時候執行是比較合理的。
混合持久化
如果開啓了混合持久化,AOF 在重寫時,不再是單純將內存數據轉換爲 RESP 命令寫入 AOF 文件,而是將重寫這一刻之前的內存做 RDB 快照處理,並且將 RDB 快照內容和增量的 AOF 修改內存數據的命令存在一起,都寫入新的 AOF 文件,新的 AOF 文件會原子覆蓋掉原來的 AOF 文件。
開啓混合持久化命令
aof-use-rdb-preamble yes # 開啓混合持久化
appendonly.aof 文件中同時保存着 RDB 和 AOF 兩種格式的數據
RDB、AOF、混合持久化對比
二:redis 主從複製
Redis 主從複製的內容包括:如何使用主從複製、主從複製的原理(重點是全量複製和部分複製、以及心跳機制)、實際應用中需要注意的問題(如數據不一致問題、複製超時問題、複製緩衝區溢出問題)、主從複製相關的配置(重點是 repl-timeout、client-output-buffer-limit slave)等。
主從複製的概述
總體過程
-
主節點負責寫數據,從節點負責讀數據。
-
主從複製,是指將一臺 Redis 服務器的數據,複製到其他的 Redis 服務器。
-
前者稱爲主節點 (master),後者稱爲從節點 (slave);
-
數據的複製是單向的,只能由主節點到從節點。
默認情況下,每臺 Redis 服務器都是主節點;且一個主節點可以有多個從節點 (或沒有從節點),但一個從節點只能有一個主節點。
主從複製的作用
-
數據冗餘:主從複製實現了數據的熱備份,是持久化之外的一種數據冗餘方式,數據可以保證可用性和一致性。
-
故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復;實際上是一種服務的冗餘。
-
負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務(即寫 Redis 數據時應用連接主節點,讀 Redis 數據時應用連接從節點),分擔服務器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高 Redis 服務器的併發量。
-
高可用基石:除了上述作用以外,主從複製還是哨兵和集羣能夠實施的基礎,因此說主從複製是 Redis 高可用的基礎。
主從複製的拓撲
一主一從
- 主節點故障轉移從節點,當主節點的 “寫” 命令併發高且需要持久化,可以只在從節點開啓 AOF(主節點不需要),這樣即保證了數據的安全性,也避免 AOF 持久化對主節點的影響
一主多從
- 針對 “讀” 較多的場景,“讀” 由多個從節點來分擔,但節點越多,主節點同步到多節點的次數也越多,影響帶寬,也加重主節點的負擔
樹狀主從:
- 一主多從的缺點(主節點推送次數多壓力大)可用些方案解決,主節點只推送一次數據到從節點 B,再由從節點 B 推送到 C,減輕主節點推送的壓力。
主從複製的原理
瞭解 redis 複製原理對日後運維有很大幫助,包括如何規劃節點,如何處理節點故障,redis 複製過程可分爲三個階段:
- 複製初始化階段
- 數據同步階段
- 命令傳播階段
複製初始化階段
主從複製主要實現的一個流程如上圖:
1. 當執行完 slaveof/replica masterip port 命令時候,從庫根據指明的 master 節點 ip 和 port 向主庫發起 socket 連接,主庫收到 socket 連接之後將連接信息保存,此時連接建立,從服務器保存主服務器的配置信息,保存之後待從服務器內部的定時器執行時,就會觸發複製的流程。
注意:replicaof 是新版本的命令,舊版本是 slaveof 命令,如果你使用的是 Redis 5.0.0 之前的版本,那麼請使用 SLAVEOF 命令代替本章中的 REPLICAOF 命令,並使用 slaveof 配置選項代替本章中的 replicaof 配置選項
2. 從服務器首先會與主服務器建立一個 socket 套字節連接,用作主從通信使用。後面主服務器發送數據給從服務器也是通過該套字節進行。
3. socket 套字節連接成功之後,接着發送鑑權 ping 命令,正常的情況下,主服務器會發送對應 pong 的響應。ping 命令的作用是爲了,保證 socket 套字節是否可以用,同時也是爲了驗證主服務器是否接受操作命令,否則可能出現超時或者主庫此時在處理其他任務阻塞那麼此時從庫將斷開 socket 連接,然後進行重試
4. 如果主庫連接設置了密碼,則從庫需要設置 masterauth 參數,此時從庫會發送 auth 命令,命令格式爲 “auth + 密碼” 進行密碼驗證,其中密碼爲 masterauth 參數配置的密碼,需要注意的是如果主庫設置了密碼驗證,從庫未配置 masterauth 參數則報錯,socket 連接斷開。當身份驗證完成以後,從節點發送自己的監聽端口,主庫保存其端口信息,此時進入下一個階段:數據同步階段
5. 可以開始複製數據了。主服務器此時會進行全量複製,將主服務的數據全部發給從服務器,從服務器保存主服務器發送的數據。
6. 接下來就是持續複製操作。主服務器會進行異步複製,一邊將寫的數據寫入自身,同時會將新的寫命令發送給從服務器。
數據同步階段
-
主庫和從庫都確認對方信息以後,便可開始數據同步,此時從庫向主庫發送 psync 命令 (需要注意的是 redis4.0 版本對 2.8 版本的 psync 做了優化,後續會進行說明),
-
主庫收到該命令後判斷是進行增量複製還是全量複製,然後根據策略進行數據的同步,當主庫有新的寫操作時候,此時進入複製第三階段:命令傳播階段。
命令傳播階段
-
當數據同步完成以後,在此後的時間裏主從維護着心跳檢查來確認對方是否在線,每隔一段時間(默認 10 秒,通過 repl-ping-replica-period/repl-ping-slave-period 參數指定)主節點向從節點發送 PING 命令判斷從節點是否在線。
-
從節點每秒 1 次向主節點發送 REPLCONF ACK 命令,命令格式爲:REPLCONF ACK {offset},其中 offset 指從節點保存的複製偏移量。
- 作用一是彙報自己複製偏移量,主節點會對比複製偏移量向從節點發送未同步的命令。
- 作用二在於判斷主節點是否在線,從庫接送命令並執行,最終實現與主庫數據相同。
主從樂觀複製機制
Redis 採用量樂觀複製策略,容忍在一定時間內主從數據內容是不同的,但是兩者的數據最終會同步。
主從複製實現策略
Redis 主從複製主要分爲三種弄策略方式,不同的策略方式都是針對不同的場景下進行使用。
三種場景方式分別如下:
全量複製
全量複製用在主從複製剛建立時或者從切主服務器時,從服務器沒有主服務器的數據,主服務器會將自身的數據通過 rdb 文件方式發送給從服務器,從服務器會清空自身數據,接着將主服務器發送的數據加載到自身中。
1. 從服務器->主服務器: 1.psync ? -1
2. 主服務器->從服務器: 2.fullsync runid offset
3. 從服務器: 保存 runid offset
4. 主服務器: 執行bgsave生成rdb
5. 主服務器->從服務器: 發送rdb
6. 從服務器: 清空自身老數據
7. 從服務器: 加載主服務器數據
部分複製
部分複製用在一些異常情況下,例如主從延遲、從服務宕機之後重新啓動接收主服務器發送的部分數據。
部分複製的實現主要依賴於複製緩存區、主服務的 runid、主從服務器各自複製偏移量 (offset)。
複製緩存區 **
repl_backlog_size:保存在主節點上的一個固定長度的先進先出隊列,默認大小是 1MB。
-
主服務在接收寫命令時,會將命令寫入緩存區,以便從服務器在異常情況下,減少數據的丟失。
-
當從服務器正常連接之後,主服務器會將緩存區內的數據發送給從服務器。這裏的緩存區是一個長隊列。
-
主節點發送數據給從節點過程中,主節點還會進行一些寫操作,這時候的數據存儲在複製緩衝區中。從節點同步主節點數據完成後,主節點將緩衝區的數據繼續發送給從節點,用於部分複製。
-
主節點響應寫命令時,不但會把寫命令發送給從節點,還會寫入複製積壓緩衝區,用於複製命令丟失的數據補救。
主服務器 runid
- 主服務器會在每次服務啓動之後,會生成一個唯一的 ID,作爲自身標識。從服務器會將該標識保存起來,發送部分複製命令時,會使用該 runid。
psync runid offset
主從複製各自偏移量:
主從服務在建立複製之後,都會有自身的偏移量 offset。
-
主節點在每次進行了寫命令之後,也會增加自身的偏移量(offset=offset + 命令的字節長度)。這裏的偏移量是通過命令的字節長度累加計算。
-
從節點會每秒鐘發送自身複製的偏移量給主節點,主節點在發送寫命令之後,從節點也會增加自身的複製偏移量(也會增加自己的 offset)。
主節點同時保存自己的 offset 和從節點的 offset,通過對比 offset 來判斷主從節點數據是否一致。
異步複製
異步複製是針對主從建立複製關係之後,主從服務器持續保持複製關係。
場景問題
數據安全
- 從服務器只讀
replica-read-only yes
- 從服務器連接密碼
masterauth
數據延遲。
默認的情況下主節點存在新數據不會立即發送給從服務器,如果開啓,則會理解延遲發送給從服務器。默認的時間間隔與 Linux 內核定義有關。
- 複製延遲
repl-disable-tcp-nodelay yes
主從節點連接狀態。
主從節點一旦建立連接之後,會定時模擬成對方的客戶端,檢測對方的服務狀態。主節點可以通過設置 repl-ping-replica-period/repl-ping-slave-period 配置參數進行設置。默認的頻率是 10s。
從節點咋執行 **replconf ack {offsetid}** 時,也會將自身的複製偏移量發送給主服務器,主服務根據偏移量進行判斷數據延遲。存在數據延遲就會從複製積壓緩衝區的數據匯中,將對應的數據補發給從節點。
三:redis sentinel 原理
Sentinel出現的前提背景
Redis持久化機制和Redis主從架構的相輔相成實現了Redis的數據高可用性以及服務的可擴展性和負載性,但是隻依靠持久化方案和主從複製能力(負載和數據的榮譽),在出現服務宕機的時候,故障切換無法自動去實現,還需要手工,這對人工成本造成了巨大的損失以及不穩定性。
持久化+主從複製 仍存在的痛點
當 主服務器下線後無法恢復服務使用主從複製,在master節點下線後,只能夠手動將 slave 節點切換爲 master,但是不能自動完成故障轉移。
Sentinel的加入
Sentinel(哨兵)是Redis的高可用性解決方案:由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,並在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級爲新的主服務器。
主從持久化機制與加入哨兵之後的對比:
Sentinel的主要功能
Redis Sentinel爲Redis提供了完整的高可用解決方案。實際上這意味着使用Sentinel可以部署一套Redis,在沒有人爲干預的情況下去應付各種各樣的失敗事件。同時提供了一些其他的功能,例如:監控、通知、併爲client提供配置。
Sentinel的概念定義
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案 ,當用Redis做Master-slave的高可用方案時,假如master宕機了,Redis本身(包括它的很多客戶端)都沒有實現自動進行主備切換,而Redis-sentinel本身也是一個獨立運行的進程,它能監控多個master-slave集羣,發現master宕機後能進行自動切換。
Redis從 2.8發佈了一個穩定版本的Redis Sentinel 。當前版本的 Sentinel稱爲Sentinel 2。它是使用更強大和更簡單的預測算法來重寫初始Sentinel實現。(Redis2.6版本提供Sentinel 1版本,但是有 一些問題)。
Sentinel的功能分佈
-
監控(Monitoring):Sentinel會不斷的檢查你的主節點和從節點是否正常工作。
-
通知(Notification):被監控的Redis實例如果出現問題,Sentinel可以通過API(pub)通知系統管理員或者其他程序。
-
自動故障轉移(Automatic failover):如果一個主節點沒有按照預期工作,Sentinel 會開始進行故障轉移,把一個從節點提升爲主節點,並重新配置其他的從節點使用新的主節點,其他的從節點會開始複製新的主節點,並且使用Redis服務的應用程序在連接的時候也被通知新的地址。
-
配置提供(Configuration provider):客戶端可以把 Sentinel 作爲權威的配置發佈者來獲得最新的maste 地址。如果發生了故障轉移,Sentinel集羣會通知客戶端新的master地址,並刷新 Redis 的配置。(sentinel會返回最新的master地址)
Sentinel的分佈特性
-
如果只使用單個sentinel進程來監控redis集羣是不可靠的,當sentinel進程宕掉後(sentinel本身也有單點問題,single-point-of-failure)整個集羣系統將無法按照預期的方式運行。所以有必要將sentinel集羣。
-
Redis Sentinel是一個分佈式系統,Sentinel運行在有許多Sentinel進程互相合作的環境下,它本身就是這樣被設計的。有許多Sentinel進程互相合作的優點如下:
-
當多個Sentinel同意一個master不再可用的時候,就執行故障檢測。這明顯降低了錯誤概率。
-
即使並非全部的Sentinel都在工作,Sentinel也可以正常工作,這種特性,讓系統非常的健康(最好是奇數個,因爲不容易選舉成爲同票)。
分佈方式總體深入下圖所示:
Sentinel的基本原理
總體而言:多個 Sentinel 進程(progress), 這些進程使用流言協議(gossip protocols)來 接收關於主服務器是否下線的信息, 並使用投票協議(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪個從服務器作爲新的主服務器。
Sentinel的主觀下線(SDOWN)
一個服務器必須在 master-down-after-milliseconds 毫秒內, 一直返回無效回覆纔會被 Sentinel 標記爲主觀下線。
-
在Sentinel哨兵的運行階段,(其會向其他的Sentinel哨兵、master和slave發送消息確認其是否存活),如果在指定的時間內未收到正常回應,暫時認爲對方掛起了(被標記爲主觀宕機–SDOWN)。
-
【注意:當只有單個sentinel實例對redis實例做出無響應的判斷,此時進入主觀判斷,不會觸發自動故障轉移等操作。】
-
【注意:一個服務器必須在 master-down-after-milliseconds 毫秒內, 一直返回無效回覆纔會被 Sentinel 標記爲主觀下線】
Sentinel的客觀下線(ODOWN)
-
當多個Sentinel哨兵(數量由quorum參數設定)都報告同一個master沒有響應了,通過投票算法(Raft算法),系統判斷其已死亡(被標記爲客觀宕機–ODOWN)。
-
多個 Sentinel 實例在對同一個服務器做出 SDOWN 判斷, 並且通過 SENTINEL is-master-down-by-addr 命令互相交流之後, 得出的服務器下線判斷。
-
Sentinel可以通過向另一個 Sentinel 發送 SENTINEL is-master-down-by-addr 命令來詢問對方是否認爲給定的服務器已下線。
Sentinel下線操作
-
從主觀下線狀態切換到客觀下線狀態並沒有使用嚴格的法定人數算法(strong quorum algorithm), 而是使用了流言協議: 如果 Sentinel 在給定的時間範圍內(master_down_after_milliseconds), 從其他 Sentinel 那裏接收到了足夠數量的主服務器下線報告, 那麼 Sentinel 就會將主服務器的狀態從主觀下線改變爲客觀下線。 如果之後其他 Sentinel 不再報告主服務器已下線, 那麼客觀下線狀態就會被移除。
-
客觀下線條件只適用於主服務器: 對於任何其他類型的 Redis 實例(其他sentinel和slave服務節點), Sentinel 在將它們判斷爲下線前不需要進行協商, 所以從服務器Slave或者其他 Sentinel 永遠不會達到客觀下線條件。
Sentinel的主從切換
- 此時Sentinel集羣會選取領頭的哨兵(leader)進行故障恢復,從現有slave節點中選出(算法後續有介紹)一個提升爲Master,並把剩餘Slave都指向新的Master,繼續維護主從關係。
Sentinel自動發現機制
-
那麼,Sentinel集羣的機器是如何發現集羣中的其他機器呢?
- 使用廣播?很顯然不合適,既然是redis的產品,自然要充分運用redis功能,Sentinel集羣節點利用了Redis master的發佈/訂閱機制去自動發現其它節點。
每個Sentinel使用發佈/訂閱的方式持續地傳播master的配置版本信息,配置傳播的發佈/訂閱管道是: sentinel:hello,我們可以通過訂閱其頻道查看頻道中的消息,如下:
Sentinel 利用 pub/sub(發佈/訂閱):
訂閱了每個 master 和 slave 數據節點的 sentinel:hello 頻道,去自動發現其它也監控了統一 master 的 sentinel 節點,Sentinel 向每 1s 向 sentinel:hello 中發送一條消息,包含了其當前維護的最新的master 配置。
-
如果某個sentinel發現自己的配置版本低於接收到的配置版本,則會用新的配置更新自己的 master 配置與發現的 Sentinel 之間相互建立命令連接,之後會通過這個命令連接來交換對於 master 數據節點的看法。
-
sentinel的狀態會被持久化地寫入sentinel的配置文件中。每次當收到一個新的配置時,或者新創建一個配置時,配置會被持久化到硬盤中,並帶上配置的版本戳。這意味着,可以安全的停止和重啓sentinel進程。
Sentinel的發現方式
原理中提及到了,當sentinel發現主庫客觀下線時候會進行領頭哨兵選舉(超過半數切大於閾值)進行故障恢復,其選舉算法採用Raft算法,這也爲什麼說其設計思想類似與zookpeer,選舉過程大體如下:
-
發現主庫客觀下線的哨兵節點(這裏稱爲A)向每個哨兵節點發送命令要求對方選舉自己爲領頭哨兵(leader);
-
如果目標哨兵沒有選舉過其他人,則同意將A選舉爲領頭哨兵;
-
如果A發現有超過半數且超過quorum參數值的哨兵節點同意選自己成爲領頭哨兵,則A哨兵成功選舉爲領頭哨兵。
-
【sentinel集羣執行故障轉移時需要選舉leader,此時涉及到majority,majority 代表 sentinel 集羣中大部分 sentinel 節點的個數,只有大於等於 max(quorum, majority) 個節點給某個 sentinel 節點投票,才能確定該sentinel節點爲leader,majority 的計算方式爲:num(sentinels) / 2 + 1】
-
當有多個哨兵節點同時參與領頭哨兵選舉時,出現沒有任何節點當選可能,此時每個參選節點等待一個隨機時間進行下一輪選舉,直到選出領頭哨兵。
故障恢復時從Slave中間選出Master的算法
-
按照slave優先級進行排序,slave-priority越低,優先級就越高;
-
如果slave priority相同,那麼比較複製偏移量,offset越靠後(越大)則表明和舊的主庫數據同步越接近,優先級就越高 ;
-
如果上面兩個條件都相同,那麼選擇一個run id最小的從庫;
主要根據slave-priority進行排序做控制選舉,先比較slave_offset值越大優先級越高,如果相等在獲取runid最小的(代表啓動時間越早)。
Sentinel(哨兵)的運作流程
-
每個Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他 Sentinel 實例發送一個 PING 命令。(心跳機制)
-
如果一個實例(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被 Sentinel 標記爲主觀下線。
-
如果一個Master被標記爲主觀下線,則正在監視這個Master的所有 Sentinel 要以每秒一次的頻率確認Master的確進入了主觀下線狀態。(確認投票下線)
-
當有足夠數量的 Sentinel(大於等於配置文件指定的值)在指定的時間範圍內確認Master的確進入了主觀下線狀態, 則Master會被標記爲客觀下線 。
-
在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有Master,Slave發送 INFO 命令。(同步數據)
-
當Master被 Sentinel 標記爲客觀下線時,Sentinel 向下線的 Master 的所有 Slave 發送 INFO 命令的頻率會從 10 秒一次改爲每秒一次。
-
若沒有足夠數量的 Sentinel 同意 Master 已經下線, Master 的客觀下線狀態就會被移除。
-
若 Master 重新向 Sentinel 的 PING 命令返回有效回覆, Master 的主觀下線狀態就會被移除。
Sentinel部署配置
-
redis源碼中提供了 sentinel 配置的模板:sentinel.conf
-
Sentinel部署很簡單,只需要配置一下/etc/redis-sentinel.conf配置文件就可以了,如下
#工作端口 port 26379 #工作目錄 dir "/var/lib/redis/sentinel" #sentinel id ,建議註釋掉,會自動生成 #sentinel myid 827f0104ad153f34db5a29b8cbb51ef21a31d6d5 #配置要監控的master名字和地址,最後一個2代表當sentinel集羣中有2個sentinel認爲master故障時候才判定master真正不可用。 官方把該參數稱爲quorum,在後續選舉領頭哨兵時候會用到 sentinel monitor mymaster 10.130.2.155 6379 2 #配置master密碼:配置主服務器的密碼(如沒設置密碼,可以省略) sentinel auth-pass mymaster Password #日誌 logfile "/var/log/redis/sentinel.log" 配置完成後,使用systemctl start redis-sentinel啓動即可。 Sentinel可以調整的相關參數 #主觀SDOWN時間,單位毫秒,默認30秒。(心跳檢測) sentinel down-after-milliseconds mymaster 30000 #在發生failover主備切換時候,最多允許多少個slave同時同步新的master。這個數字越小,完成failover所需的時間就越長,但是如果這個數字越大,就意味着越多的slave因爲replication而不可用。可以通過將這個值設爲 1 來保證每次只有一個slave處於不能處理命令請求的狀態。 sentinel parallel-syncs mymaster 1 #failover-time超時時間,當failover開始後,在此時間內仍然沒有觸發任何failover操作,當前sentinel將會認爲此次failover失敗,單位毫秒。默認3分鐘。 sentinel failover-timeout mymaster 180000
核心配置
sentinel monitor <master-name> <ip> <redis-port> <quorum>: 監控的 redis 主節點 #配置主服務器的密碼(如沒設置密碼,可以省略) sentinel auth-pass mymaster 123456 #修改心跳檢測 5000毫秒 sentinel down-after-milliseconds mymaster 5000
- sentinel 是 redis 配置的提供者,而不是代理,客戶端只是從 sentinel 獲取數據節點的配置,因此這裏的 ip 必須是 redis 客戶端能夠訪問的。
Sentinel 啓動
雖然哨兵(sentinel) 釋出爲一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你可以在啓動一個普通 Redis 服務器時通過給定 --sentinel 選項來啓動哨兵(sentinel)。
如果你使用redis-sentinel可執行文件,你可以使用下面的命令來運行Sentinel:
$ redis-sentinel /path/to/sentinel.conf
當然也可以採用 redis服務的方式啓動:
$ redis-server sentinel.conf --sentinel &
兩種方式是一樣的。
不管咋樣,使用一個配置文件來運行Sentinel是必須的,這個文件被系統使用來存儲當前狀態,如果重啓,這些狀態會被重新載入。如果沒有配置文件或者配置文件的路徑不對,Sentinel將會拒絕啓動。
默認情況下,Sentinels監聽TCP端口26379,所以爲了讓Sentinels運行,你的機器的26379端口必須是打開的,用來接收其他Sentinel實例的連接,否則,Sentinels不能互相交流,也不知道該幹什麼,也不會執行故障轉移。
-
初始化一個普通的redis服務器
-
加載Sentinel專用配置,例如命令表、參數等,Sentinel 使用 sentinel.c 中的命令表、函數等配置,普通 Redis 則使用 redis.c 中的配置
-
除了保存服務器一般狀態之外,Sentinel 還會保存 Sentinel 相關狀態
注意:
1 .當啓動哨兵模式之後,如果你的master服務器宕機之後,哨兵自動會在從redis服務器裏面 投票選舉一個master主服務器出來;這個主服務器也可以進行讀寫操作!
-
如果之前宕機的主服務器已經修好,可以正式運行了。那麼這個服務器只能進行讀的操作,會自動跟隨由哨兵選舉出來的新服務器!
-
大家可以進入./redis-cli,輸入info,查看你的狀態信息;
持久化仍存在的問題:
-
一旦主節點宕機,從節點晉升成主節點,同時需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預。[哨兵已解決]
-
節點的寫能力受到單機的限制。[集羣已解決]
-
節點的存儲能力受到單機的限制。[集羣已解決]
那麼,redis 集羣是如何實現高可用的,它又如何部署呢?