Redis 高可用理論
Redis 高可用
- 在web伺服器中,高可用是指伺服器可以正常訪問的時間,衡量的標準是在多長時間內可以提供正常服務(99.9%、99.99%、99.999%等等)
- 但是在Redis語境中,高可用的含義似乎要寬泛一些,除了保證提供正常服務(如主從分離、快速容災技術),還需要考慮資料容量的擴充套件、資料安全不會丟失等。
一、概論
在Redis中,實現高可用的技術主要包括持久化
、主從複製、哨兵和叢集。
- 持久化:持久化是最簡單的高可用方法,主要作用是資料備份,即將資料儲存在硬碟,保證資料不會因程序退出而丟失。
缺陷:因為功能單一,有時甚至不被歸為高可用的手段- 主從複製:主從複製是高可用Redis的基礎,哨兵和叢集都是在主從複製基礎上實現高可用的。主從複製主要實現了資料的多機備份,以及對於讀操作的負載均衡和簡單的故障恢復。
缺陷:故障恢復無法自動化;寫操作無法負載均衡;儲存能力受到單機的限制。- 哨兵:在主從複製的基礎上,哨兵實現了自動化的故障恢復。
缺陷:寫操作無法負載均衡;儲存能力受到單機的限制。- 叢集:通過叢集,Redis解決了寫操作無法負載均衡,以及儲存能力受到單機限制的問題,實現了較為完善的高可用方案
。
一、Redis持久化
持久化的功能:Redis是記憶體資料庫,資料都是儲存在記憶體中。為了避免伺服器斷電等原因導致Redis程序異常退出後資料的永久丟失,需要定期將Redis中的資料以某種形式(資料或命令)從記憶體儲存到硬碟;當下次Redis重啟時,利用持久化檔案實現資料恢復。除此之外,為了進行災難備份,可以將持久化檔案拷貝到一個遠端位置。
Redis 兩種持久化方式
- RDB持久化:原理是將 Reids在記憶體中的資料庫記錄定時儲存到磁碟上。
- AOF持久化:(append only file):原理是將Reids 的操作日誌以追加的方式寫入檔案,類似於MySQL的binlog。
由於AOF持久化的實時性
更好,即當程序意外退出時丟失的資料更少,因此AOF是目前主流的持久化方式,不過RDB持久化仍然有其用武之地
1.1 RDB 持久化
RDB持久化是指在指定的時間間隔內將記憶體中當前程序中的資料生成快照儲存到硬碟(因此也稱作快照持久化),用二進位制壓縮儲存,儲存的檔案字尾是rdb;當Redis重新啟動時,可以讀取快照檔案恢復資料。
(RDB速度快,類似於快照)
觸發條件
RDB持久化的觸發分為手動觸發和自動觸發兩種。
-
手動觸發
- save命令和bgsave命令都可以生成RDB檔案。
- save命令會阻塞Redis伺服器程序,直到RDB檔案建立完畢為止,在Redis伺服器阻塞期間,伺服器不能處理任何命令請求。
- 而bgsave命令會建立一個子程序,由子程序來負責建立RDB檔案,父程序(即Redis主程序)則繼續處理請求。
- bgsave命令執行過程中,只有fork子程序時會阻塞伺服器,而對於save命令,整個過程都會阻塞伺服器,因此save已基本被廢棄,線上環境要杜絕save的使用。
-
自動觸發
- 在自動觸發RDB持久化時,Redis也會選擇bgsave而不是save來進行持久化。
save m n #自動觸發最常見的情況是在配置檔案中通過save m n,指定當m秒內發生n次變化時,會觸發bgsave。
vim /etc/redis/6379.conf
--219行--以下三個save條件滿足任意一個時,都會引起bgsave的呼叫
save 900 1 :當時間到900秒時,如果redis資料發生了至少1次變化,則執行bgsave
save 300 10 :當時間到300秒時,如果redis資料發生了至少10次變化,則執行bgsave
save 60 10000 :當時間到60秒時,如果redis資料發生了至少10000次變化,則執行bgsave
--254行--指定RDB檔名
dbfilename dump.rdb2
--264行--指定RDB檔案和AOF檔案所在目錄
dir /var/lib/redis/6379
-242行--是否開啟RDB檔案壓縮
rdbcompression yes
- 其他自動觸發機制
除了savemn以外,還有一些其他情況會觸發bgsave:- 在主從複製場景下,如果從節點執行全量複製操作,則主節點會執行bgsave命令,並將rdb檔案傳送給從節點。
- 執行shutdown命令時,自動執行rdb持久化。
執行流程
- Redis父程序首先判斷:當前是否在執行save,或bgsave/bgrewriteaof的子程序,如果在執行則bgsave命令直接返回。bgsave/bgrewriteaof的子程序不能同時執行,主要是基於效能方面的考慮:兩個併發的子程序同時執行大量的磁碟寫操作,可能引起嚴重的效能問題。
- 父程序執行fork操作建立子程序,這個過程中父程序是阻塞的,Redis不能執行來自客戶端的任何命令
- 父程序fork後,bgsave命令返回"Background saving started" 資訊並不再阻塞父程序,並可以響應其他命令
- 子程序建立RDB檔案,根據父程序記憶體快照生成臨時快照檔案,完成後對原有檔案進行原子替換
- 子程序傳送訊號給父程序表示完成,父程序更新統計資訊
啟動時載入
RDB檔案的載入工作是在伺服器啟動時自動執行的,並沒有專門的命令。但是由於AOF的優先順序更高,因此當AOF開啟時,Redis 會優先載入AOF檔案來恢復資料;只有當AOF關閉時,才會在Redis伺服器啟動時檢測RDB檔案,並自動載入。伺服器載入RDB檔案期間處於阻塞狀態,直到載入完成為止。
Redis載入RDB檔案時,會對RDB檔案進行校驗,如果檔案損壞,則日誌中會列印錯誤,Redis啟動失敗。
1.2 AOF持久化
RDB持久化是將程序資料寫入檔案,而AOF持久化,則是將Redis執行的每次寫、刪除命令記錄到單獨的日誌檔案中,查詢操作不會記錄:當Redis重啟時再次執行AOF檔案中的命令來恢復資料。
與RDB相比,AOF的實時性更好,因此已成為主流的持久化方案。
開啟AOF
Redis伺服器預設開啟RDB,關閉AOF;要開啟AOF,需要在配置檔案中配置:
vim /etc/redis/6379.conf
--700行--修改,開啟AOF
appendonly yes
--704行--指定AOF檔名稱
appendfilename "appendonly.aof"
--796行--是否忽略最後一條可能存在問題的指令
aof-load-truncated yes
/etc/init.d/redis_6379 restart
執行流程
由於需要記錄Redis的每條寫命令,因此AOF不需要觸發,下 面介紹AOF的執行流程。
AOF的執行流程包括:
- 命令追加(append):將Redis的寫命令追加到緩衝區aof_buf;
- 檔案寫入(write)和檔案同步(sync):根據不同的同步策略將aof_buf中的內容同步到硬碟:
- 檔案重寫(rewrite):定期重寫AOF檔案,達到壓縮的目的。
(1)命令追加(append)
Redis先將寫命令追加到緩衝區,而不是直接寫入檔案,主要是為了避免每次有寫命令都直接寫入硬碟,導致硬碟To成為Redis負載的瓶頸
命令追加的格式是Redis命令請求的協議格式,它是一種純文字格式,具有相容性好、可讀性強、容易處理、操作簡單避免二次開銷等優點。在AOF檔案中,除了用於指定資料庫的select命令(如select 0為選中0號資料庫)是由Redis新增的,其他都是客戶端傳送來的寫命令。
(2)檔案寫入(write)和檔案同步(sync)
Redis提供了多種AoF快取區的同步檔案策略,策略涉及到作業系統的write函式和fsync函式,說明如下:
為了提高檔案寫入效率,在現代作業系統中,當用戶呼叫write函式將資料寫入檔案時,作業系統通常會將資料暫存到一個記憶體緩衝區裡,當緩衝區被填滿或超過了指定時限後,才真正將緩衝區的資料寫入到硬盤裡。這樣的操作雖然提高了效率,但也帶來了安全問題:如果計算機停機,記憶體緩衝區中的資料會丟失;因此係統同時提供了fsync、fdatasync等同步函式,可以強制作業系統立刻將緩衝區中的資料寫入到硬盤裡,從而確保資料的安全性。
AOF快取區的同步檔案策略存在三種同步方式,它們在配置檔案的729-731行,分別是:
- appendfsync always:
命令寫入aof_buf後立即呼叫系統fsync操作同步到AOF檔案,fsync完成後執行緒返回。這種情況下,每次有寫命令都要同步到AOF檔案,硬碟IO成為效能瓶頸,Redis只 能支援大約幾百TPS寫入,嚴重降低了Redis的效能;即便是使用固態硬碟(SSD) ,每秒大約也只能處理幾萬個
命令,而且會大大降低SSD的壽命。
- appendf synceverysec:
命令寫入aof_ buf後呼叫系統write操作,write完成後執行緒返回; fsync同步檔案操作由專門的執行緒每秒呼叫一次。 everysec是前述兩種策
略的折中,是效能和資料安全性的平衡,因此是Redis的預設配置,也是我們推薦的配置。
- appendfsync no:
命令寫入aof_buf後呼叫系統write操作,不對AOF檔案做fsync同步;同步由作業系統負責,通常同步週期為30秒。這種情況下,檔案同步的時間不可控,且緩衝區中堆積的資料會很多,資料安全性無法保證。
(3)檔案重寫(rewrite)
隨著時間流逝,Redis伺服器執行的寫命令越來越多,AOF檔案也會越來越大;過大的AOF檔案不僅會影響伺服器的正常執行,也會導致資料恢復需要的時間過長。
檔案重寫是指定期重寫AOF檔案,減小AOF檔案的體積。需要注意的是,AOF重寫是把Redis程序內的資料轉化為寫命令,同步到新的AOF檔案;不會對舊的AOF檔案進行任何讀取、寫入操作!
關於檔案重寫需要注意的另一點是:對於AOF持久化來說,檔案重寫雖然是強烈推薦的,但並不是必須的:即使沒有檔案重寫,資料也可以被持久化並在Redis啟動的時候匯入;因此在一些實現中, 會關閉自動的檔案重寫,然後通過定時任務在每天的某一時 刻定時執行。
檔案重寫之所以能夠壓縮AOF檔案,原因在於:
- 過期的資料不再寫入檔案
- 無效的命令不再寫入檔案:如有些資料被重複設值(set mykey v1, set mykey v2)、有些資料被刪除了(set myset v1, del myset)等。
- 多條命令可以合併為一個:如sadd myset v1, sadd myset v2,sadd myset v3可以合併為sadd myset v1 v2 v3。
通過上述內容可以看出,由於重寫後AOF執行的命令減少了,檔案重寫既可以減少檔案佔用的空間,也可以加快恢復速度。
檔案重寫的觸發,分為手動觸發和自動觸發:
- 手動觸發:直接呼叫bgrewriteaof命令,該命令的執行與bgsave有些類似: 都是fork子程序進行具體的工作,且都只有在fork時阻塞。
- 自動觸發:通過設定auto-aof- rewrite-min-size選項和auto-aof- rewrite-percentage選項來自動執行BGREWRITEAOF。
只有當auto-aof-rewrite-min-size和auto-aof-rewrite- percentage兩個選項同時滿足時,才會自動觸發AOF重寫,即bgrewriteaof操作。
vim /etc/redis/6379.conf
- auto-aof-rewrite-percentage 100:當前AOF檔案大小(即aof_current_ size)是上次日誌重寫時AOF檔案大小(aof_base_ size)兩倍時,發生BGREWRITEAOF操作
- auto-aof-rewrite-min-size 64mb:當前AOF檔案執行BGREWRITEAOF命令的最小值,避免剛開始啟動Reids時由於檔案尺寸較小導致頻繁的BGREWRITEAOF
關於檔案重寫的流程,有兩點需要特別注意:
- 重寫由父程序fork子程序進行;
- 重寫期間Redis執行的寫命令,需要追加到新的AOF檔案中,為此Redis引入了aof_rewrite_buf快取。
檔案重寫的流程如下:
- Redis父程序首先判斷當前是否存在正在執行bgsave/bgrewriteaof的子程序,如果存在則bgrewriteaof命令直接返回,如果存在bgsave命令則等bgsave執行完成後再執行。
- 父程序執行fork操作建立子程序,這個過程中父程序是阻塞的。
- 父程序fork後,bgrewriteaof命令返回"Background append only file rewrite started"資訊並不再阻塞父程序,並可以響應其他命令。Redis的所有寫命令依然寫入AOF緩衝區,並根據appendfsync策略同步到硬碟,保證原有AOF機制的正確。
- 由於fork操作使用寫時複製技術,子程序只能共享fork操作時的記憶體資料。由於父程序依然在響應命令,因此Redis使用AoF重寫緩衝區(aof_rewrite buf)儲存這部分資料,防止新AOF檔案生成期間丟失這部分資料。也就是說,bgrewriteaof執行期間,Redis的寫命令時追加到aof_buf和aof_rewirte_buf兩個緩衝區。
- 子程序根據記憶體快照,按照命令合併規則寫入到新的AOF檔案。
- 子程序寫完新的AOF檔案後,向父程序發訊號,父程序更新統計資訊,具體可以通過info persistence檢視。
- 父程序把AOF重寫緩衝區的資料寫入到新的AOF檔案,這樣就保證了新AOF檔案所儲存的資料庫狀態和伺服器當前狀態一致。
- 使用新的AOF檔案替換老檔案,完成AOF重寫
啟動時載入
當AOF開啟時,Redis啟 動時會優先載入AOF檔案來恢復資料;只有當AOF關閉時,才會載入RDB檔案恢復資料。
當AOF開啟,但AOF檔案不存在時,即使RDB檔案存在也不會載入。
Redis載入AOF檔案時,會對AOF檔案進行校驗,如果檔案損壞,則日誌中會列印錯誤,Redis啟動失敗。但如果是AOF檔案結尾不完整(機器突然宕機等容易導致檔案尾部不完整),且aof-load-truncated引數開啟,則日誌中會輸出警告,Redis忽 略掉AOF檔案的尾部,啟動成功。aof-load-truncated引數預設是開啟的。
三、RDB和AOF的優缺點
-
RDB持久化
- 優點:RDB檔案緊湊,體積小,網路傳輸快,適合全量複製;恢復速度比AOF快很多。當然,與AOF相比,RDB最重要的優點之一是對效能的影響相對較小。
- 缺點:RDB檔案的致命缺點在於其資料快照的持久化方式決定了必然做不到實時持久化,而在資料越來越重要的今天,資料的大量丟失很多時候是無法接受的,因此AOF持久化成為主流。此外,RDB檔案需要滿足特定格式,相容性差(如老版本的Redis不相容新版本的RDB檔案)
對於RDB持久化,一方面是bgsave在進行fork操作時Redis主程序會阻塞,另一方面,子程序向硬碟寫資料也會帶來IO壓力。
-
AOF持久化
- 與RDB持久化相對應,AOF的優點在於支援秒級持久化、相容性好,缺點是檔案大、恢復速度慢、對效能影響大。
- 對於AOF持久化,向硬碟寫資料的頻率大大提高(everysec策略下為秒級),IO壓力更大,甚至可能造成AOF追加阻塞問題。
- AOF檔案的重寫與RDB的bgsave類似,會有fork時的阻塞和子程序的IO壓力問題。相對來說,由於AOF向硬碟中寫資料的頻率更高,因此對Redis主程序效能的影響會更大。
四、Redis 主從複製
主從複製,是指將一臺Redis伺服器的資料,複製到其他的Redis伺服器。前者稱為主節點(Master),後者稱為從節點(Slave):資料的複製是單向的,只能由主節點到從節點。
預設情況下,每臺Redis伺服器都是主節點:且一個主節點可以有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。
主從複製的作用:
- 資料冗餘:主從複製實現了資料的熱備份,是持久化之外的一種資料冗餘方式。
- 故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復;實際上是一種服務的冗餘。
- 負載均衡:在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務(即寫Redis資料時應用連線主節點,讀Redis資料時應用連線從節點),分擔伺服器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高Redis伺服器的併發量。
- 高可用基石:除了上述作用以外,主從複製還是哨兵和叢集能夠實施的基礎,因此說主從複製是Redis高可用的基礎。
主從複製流程:
- 若啟動一個slave機器程序,則它會向Master機器傳送一個"sync command"命令,請求同步連線.
- 無論是第一次連線還是重新連線,Master機器都會啟動一個後臺程序,將資料快照儲存到資料檔案中(執行rdb操作),同N時Master還會記錄修改資料的所有命令並快取在資料檔案中。
- 後臺程序完成快取操作之後,Master機器就會向slave機器傳送資料檔案,Slave端機器將資料檔案儲存到硬碟上,然後將其載入到記憶體中,接著Master機器就會將修改資料的所有操作一併傳送給slave端機器。若slave出現故障導致宕機,則恢復正常後會自動重新連線。
- Master機器收到Slave端機器的連線後,將其完整的資料檔案傳送給slave端機器,如果Mater同時收到多個Slave發來的同步請求,則Master會在後臺啟動一個程序以儲存資料檔案,然後將其傳送給所有的Slave端機器,確保所有的Slave端機器都正常。
五、Redis 哨兵模式
哨兵的核心功能:在主從複製的基礎上,哨兵引入了主節點的自動故障轉移。
原理:哨兵(sentinel)是一個分散式系統,用於對主從結構中的每臺伺服器進行監控,當出現故障時通過投票機制選擇新的Master並將所有 slave連線到新的 Master。所以整個執行哨兵的叢集的數量不得少於3個節點。
哨兵模式的作用:
- 監控:哨兵會不斷地檢查主節點和從節點是否運作正常。
- 自動故障轉移:當主節點不能正常工作時,哨兵會開始自動故障轉移操作,它會將失效主節點的其中一個從節點升級為新的主節點,並讓其他從節點改為複製新的主節點。
- 通知(提醒):哨兵可以將故障轉移的結果傳送給客戶端。
哨兵結構由兩部分組成,哨兵節點和資料節點:
- 哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不儲存資料。
- 資料節點:主節點和從節點都是資料節點。
哨兵的啟動依賴於主從模式,所以須把主從模式安裝好的情況下再去做哨兵模式,所有節點上都需要部署哨兵模式,哨兵模式會監控所有的Redis工作節點是否正常,當Master出現問題的時候,因為其他節點與主節點失去聯絡,因此會投票,投票過半就認為這個Master的確出現問題,然後會通知哨兵間,然後從Slaves中選取一個作為新的Master。
需要特別注意的是,客觀下線是主節點才有的概念:如果從節點和哨兵節點發生故障,被哨兵主觀下線後,不會再有後續的客觀下線和故障轉移操作。
六、Redis群集模式
叢集,即Redis Cluster,是Redis3. 0開始引入的分散式儲存方案。
叢集由多個節點(Node)組成,Redis的資料分佈在這些節點中。叢集中的節點分為主節點和從節點:只有主節點負責讀寫請求和叢集資訊的維護;從節點只進行主節點資料和狀態資訊的複製。
叢集的作用,可以歸納為兩點:
- 資料分割槽(或稱資料分片):是叢集最核心的功能。
叢集將資料分散到多個節點,一方面突破了Redis單機記憶體大小的限制,儲存容量大大增加:另一方而每個主節點都可以對外提供讀服務和寫服務,大大提高了叢集的響應能力。
Redis單機記憶體大小受限問題,在介紹持久化和主從複製時都有提及;例如,如果單機記憶體太大,bgsave和bgrewriteaof的fork操作可能導致主程序阻塞,主從環境下主機切換時可能導致從節點長時間無法提供服務,全量複製階段主節點的複製緩衝區可能溢位。- 高可用:叢集支援主從複製和主節點的自動故障轉移(與哨兵類似);當任一節 點發生故障時,叢集仍然可以對外提供服務。
Redis叢集的資料分片:
Redis叢集引入了雜湊槽的概念
Redis叢集有16384個雜湊槽(編號0-16383)
叢集的每個節點負責一部分雜湊槽
每個Key通過cRc16校驗後對16384取餘來決定放置哪個雜湊槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作
以3個節點組成的叢集為例:
節點A包含0到5460號雜湊槽
節點B包含5461到10922號雜湊槽
節點c包含10923到16383號雜湊槽
Redis叢集的主從複製模型:
叢集中具有A、B、C三個節點,如果節點B失敗了,整個叢集就會因缺少5461-10922這個範圍的槽而不可以用。
為每個節點新增一個從節點A1、B1、C1整個叢集便有三個Master節點和三個slave節點組成,在節點B失敗後,叢集選舉B1位為的主節點繼續服務。當B和B1 都失敗後,叢集將不可用。