深刻剖析Redis高可用系列:持久化 AOF和RDB

歡迎關注公衆號:「碼農富哥」,致力於分享後端技術 (高併發架構,分佈式集羣系統,消息隊列中間件,網絡,微服務,Linux, TCP/IP, HTTP, MySQL, Redis), Python 等 原創乾貨 和 面試指南!

免費視頻福利推薦:web

2T學習視頻教程+電子書 免費送:BAT面試精講視頻,億級流量秒殺系統,分佈式系統架構,中間件消息隊列,Python Go入門到精通,Java實戰項目,Linux, 網絡,MySQL高性能,Redis集羣架構,大數據,架構師速成,微服務,容器化Docker K8s, ELK Stack日誌系統等免費視頻教程!面試

Redis高可用概述

在介紹Redis高可用以前,先說明一下在Redis的語境中高可用的含義。redis

咱們知道,在web服務器中,高可用是指服務器能夠正常訪問的時間,衡量的標準是在多長時間內能夠提供正常服務(99.9%、99.99%、99.999% 等等)。可是在Redis語境中,高可用的含義彷佛要寬泛一些,除了保證提供正常服務(如主從分離、快速容災技術),還須要考慮數據容量的擴展、數據安全不會丟失等。數據庫

在Redis中,實現高可用的技術主要包括持久化、複製、哨兵和集羣,下面分別說明它們的做用,以及解決了什麼樣的問題。編程

  • 持久化:持久化是最簡單的高可用方法(有時甚至不被歸爲高可用的手段),主要做用是數據備份,即將數據存儲在硬盤,保證數據不會因進程退出而丟失。
  • 複製:複製是高可用Redis的基礎,哨兵和集羣都是在複製基礎上實現高可用的。複製主要實現了數據的多機備份,以及對於讀操做的負載均衡和簡單的故障恢復。缺陷:故障恢復沒法自動化;寫操做沒法負載均衡;存儲能力受到單機的限制。
  • 哨兵:在複製的基礎上,哨兵實現了自動化的故障恢復。缺陷:寫操做沒法負載均衡;存儲能力受到單機的限制。
  • 集羣:經過集羣,Redis解決了寫操做沒法負載均衡,以及存儲能力受到單機限制的問題,實現了較爲完善的高可用方案。

Redis持久化概述

Redis 的數據所有在內存裏,若是忽然宕機,數據就會所有丟失,所以必須有一種機制來保證 Redis 的數據不會由於故障而丟失,這種機制就是 Redis 的持久化機制。後端

Redis爲持久化提供了兩種方式:緩存

  • RDB:在指定的時間間隔能對你的數據進行快照存儲。
  • AOF:記錄每次對服務器寫的操做,當服務器重啓的時候會從新執行這些命令來恢復原始的數據。

因爲AOF持久化的實時性更好,即當進程意外退出時丟失的數據更少,所以AOF是目前主流的持久化方式,不過RDB持久化仍然有其用武之地。安全

下面依次介紹RDB持久化和AOF持久化;服務器

RDB持久化

RDB是默認的持久化方式,按照必定的策略週期性的將內存中的數據生成快照保存到磁盤。網絡

每次快照持久化都是將內存數據完整寫入到磁盤一次,並非增量的只同步髒數據。若是數據量大的話,並且寫操做比較多,必然會引發大量的磁盤io操做,可能會嚴重影響性能。

1. 工做原理:

  • Redis調用fork(),產生一個子進程。
  • 子進程把數據寫到一個臨時的RDB文件。
  • 當子進程寫完新的RDB文件後,把舊的RDB文件替換掉。

2. 觸發機制

RDB觸發持久化分爲手動觸發和自動觸發

  1. save 命令(手動觸發)

當客戶端向Redis server發送save命令請求進行持久化時,因爲Redis是用一個主線程來處理全部,save命令會阻塞Redis server處理其餘客戶端的請求,直到數據同步完成。save命令會阻塞Redis服務器進程,直到RDB文件建立完畢爲止,在Redis服務器阻塞期間,服務器不能處理任何命令請求,所以線上環境不推薦使用

  1. bgsave命令(手動觸發)

與save命令不一樣,bgsave是異步執行的,當執行bgsave命令以後,Redis主進程會fork 一個子進程將數據保存到rdb文件中,同步完數據以後,對原有文件進行替換,而後通知主進程表示同步完成。

  1. 自動觸發

除了手動觸發RDB持久化,Redis內部還存在自動觸發機制,

在配置中集中配置 save m n 的方式,表示 m秒內數據集存在n次修改時,系統自動觸發bgsave 操做。 

3. RDB自動持久化配置

# 時間策略
save 900 1
save 300 10
save 60 10000

# 文件名稱
dbfilename dump.rdb

# 文件保存路徑
dir /etc/redis/data/

# 若是持久化出錯,主進程是否中止寫入
stop-writes-on-bgsave-error yes

# 是否壓縮
rdbcompression yes

# 導入時是否檢查
rdbchecksum yes
rdb持久化策略比較簡單,下面解釋一下:

save 900 1 表示900s內若是有1條是寫入命令,就觸發產生一次快照,能夠理解爲就進行一次備份
save 300 10 表示300s內有10條寫入,就產生快照
下面的相似,那麼爲何須要配置這麼多條規則呢?由於Redis每一個時段的讀寫請求確定不是均衡的,爲了平衡性能與數據安全,咱們能夠自由定製什麼狀況下觸發備份。因此這裏就是根據自身Redis寫入狀況來進行合理配置。

stop-writes-on-bgsave-error yes 這個配置也是很是重要的一項配置,這是當備份進程出錯時,主進程就中止接受新的寫入操做,是爲了保護持久化的數據一致性問題。若是本身的業務有完善的監控系統,能夠禁止此項配置, 不然請開啓。

rdbcompression yes 用於配置是否壓縮RDB文件,建議沒有必要開啓,畢竟Redis自己就屬於CPU密集型服務器,再開啓壓縮會帶來更多的CPU消耗,相比硬盤成本,CPU更值錢。

rdbchecksum yes 是否開啓RDB文件的校驗,在寫入文件和讀取文件時都起做用;關閉checksum在寫入文件和啓動文件時大約能帶來10%的性能提高,可是數據損壞時沒法發現

dbfilename dump.rdb RDB文件名

dir ./ RDB文件和AOF文件所在目錄

固然若是你想要禁用RDB配置,也是很是容易的,只須要在save的最後一行寫上:save ""

4. 執行流程圖

RDB執行流程圖

1)  Redis父進程首先判斷:當前是否在執行save,或bgsave/bgrewriteaof(後面會詳細介紹該命令)的子進程,若是在執行則bgsave命令直接返回。bgsave/bgrewriteaof 的子進程不能同時執行,主要是基於性能方面的考慮:兩個併發的子進程同時執行大量的磁盤寫操做,可能引發嚴重的性能問題。

2)  父進程執行fork操做建立子進程,這個過程當中父進程是阻塞的,Redis不能執行來自客戶端的任何命令

3)  父進程fork後,bgsave命令返回」Background saving started」信息並再也不阻塞父進程,並能夠響應其餘命令

4)  子進程建立RDB文件,根據父進程內存快照生成臨時快照文件,完成後對原有文件進行原子替換

5)  子進程發送信號給父進程表示完成,父進程更新統計信息

5. 數據恢復 & Redis啓動加載數據

RDB文件的載入工做是在服務器啓動時自動執行的,並無專門的命令。可是因爲AOF的優先級更高,所以當AOF開啓時,Redis會優先載入AOF文件來恢復數據;

只有當AOF關閉時,纔會在Redis服務器啓動時檢測RDB文件,並自動載入。服務器載入RDB文件期間處於阻塞狀態,直到載入完成爲止。

因此Redis的內存數據若是很大,會致使數據恢復時間比較長,所以線上實踐更傾向於限制單個Redis的內存不能太大,同時結合Redis Cluster集羣使用多節點部署

Redis啓動日誌中能夠看到自動載入的執行:
RDB載入過程

Redis載入RDB文件時,會對RDB文件進行校驗,若是文件損壞,則日誌中會打印錯誤,Redis啓動失敗。

你們若是更系統瞭解Redis 高可用集羣架構知識,能夠關注公衆號【碼農富哥】後回覆【Redis】獲取 Redis高可用集羣架構視頻

AOF持久化

RDB快照並非很可靠。若是你的電腦忽然宕機了,或者電源斷了,又或者不當心殺掉了進程,那麼最新的數據就會丟失。而AOF文件則提供了一種更爲可靠的持久化方式。每當Redis接受到會修改數據集的命令時,就會把命令追加到AOF文件裏,當你重啓Redis時,AOF裏的命令會被從新執行一次,重建數據。

1.工做原理

因爲須要記錄Redis的每條寫命令,所以AOF不須要觸發, AOF的執行流程包括:

  • 命令追加(append):將Redis的寫命令追加到緩衝區aof_buf;
  • 文件寫入(write)和文件同步(sync):根據不一樣的同步策略將aof_buf中的內容同步到硬盤;
  • 文件重寫(rewrite):按期重寫AOF文件,達到壓縮的目的。

AOF執行流程

2. AOF 持久化配置

# 是否開啓aof
appendonly yes

# 文件名稱
appendfilename "appendonly.aof"

# 同步方式
appendfsync everysec

# aof重寫期間是否同步
no-appendfsync-on-rewrite no

# 重寫觸發配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加載aof時若是有錯如何處理
aof-load-truncated yes

# 文件重寫策略
aof-rewrite-incremental-fsync yes

3. AOF同步策略

同步步驟分爲兩步:

  • Redis收到寫命令後首先會追加到AOF緩衝區aof_buf,而不是直接寫入文件系統,由於AOF緩衝區是內存提存的,寫入速度極高,能夠避免每次寫入命令到硬盤,致使硬盤IO成爲Redis的負載瓶頸
  • 經過調用系統函數 fsync() 把AOF緩衝區的數據真正寫到磁盤裏面持久化。因爲數據是先存儲在緩衝區內存裏面,若是碰到斷電,宕機那麼緩衝區裏面的數據沒來得急落盤就會丟失,所以咱們必須有一個相對可靠的機制保證數據落盤。

Redis寫命令寫入磁盤的命令是經過appendfsync來配置的。
appendfsync 三個取值表明三種落盤策略:

  • always:命令寫入aof緩衝區後當即調用系統fsync操做同步到AOF文件,fsync完成後線程返回。這種狀況下,每次有寫命令都要同步到AOF文件,硬盤IO成爲性能瓶頸。
  • no:命令寫入aof緩衝區後調用系統write操做,不對AOF文件作fsync同步;同步由操做系統負責,一般同步週期爲30秒。這種狀況下,文件同步的時間不可控,且緩衝區中堆積的數據會不少,數據安全性沒法保證。
  • everysec:命令寫入aof緩衝區後調用系統write操做,write完成後線程返回;fsync同步文件操做由專門的線程每秒調用一次。everysec是前述兩種策略的折中,是性能和數據安全性的平衡,所以是Redis的默認配置,也是咱們推薦的配置。

4. AOF文件重寫(rewrite)

隨着寫操做的不斷增長,AOF文件會愈來愈大。例如你遞增一個計數器100次,那麼最終結果就是數據集裏的計數器的值爲最終的遞增結果,可是AOF文件裏卻會把這100次操做完整的記錄下來。而事實上要恢復這個記錄,只須要1個命令就好了,也就是說AOF文件裏那100條命令其實能夠精簡爲1條。因此Redis支持這樣一個功能:在不中斷服務的狀況下在後臺重建AOF文件。

AOF重寫流程:

AOF重寫流程

關於文件重寫的流程,有兩點須要特別注意:

(1)重寫由父進程fork子進程進行;

(2)重寫期間Redis執行的寫命令,須要追加到新的AOF文件中,爲此Redis引入了aof_rewrite_buf緩存。

對照上圖,文件重寫的流程以下:

1) Redis父進程首先判斷當前是否存在正在執行 bgsave/bgrewriteaof的子進程,若是存在則bgrewriteaof命令直接返回,若是存在bgsave命令則等bgsave執行完成後再執行。前面曾介紹過,這個主要是基於性能方面的考慮。

2) 父進程執行fork操做建立子進程,這個過程當中父進程是阻塞的。

3.1) 父進程fork後,bgrewriteaof命令返回」Background append only file rewrite started」信息並再也不阻塞父進程,並能夠響應其餘命令。Redis的全部寫命令依然寫入AOF緩衝區,並根據appendfsync策略同步到硬盤,保證原有AOF機制的正確。

3.2) 因爲fork操做使用寫時複製技術,子進程只能共享fork操做時的內存數據。因爲父進程依然在響應命令,所以Redis使用AOF重寫緩衝區(圖中的aof_rewrite_buf)保存這部分數據,防止新AOF文件生成期間丟失這部分數據。也就是說,bgrewriteaof執行期間,Redis的寫命令同時追加到aof_buf和aof_rewirte_buf兩個緩衝區。

4) 子進程根據內存快照,按照命令合併規則寫入到新的AOF文件。

5.1) 子進程寫完新的AOF文件後,向父進程發信號,父進程更新統計信息,具體能夠經過info persistence查看。

5.2) 父進程把AOF重寫緩衝區的數據寫入到新的AOF文件,這樣就保證了新AOF文件所保存的數據庫狀態和服務器當前狀態一致。

5.3) 使用新的AOF文件替換老文件,完成AOF重寫。

重寫觸發:

  1. 手動觸發:直接調用bgrewriteaof命令,該命令的執行與bgsave有些相似:都是fork子進程進行具體的工做,且都只有在fork時阻塞。
  2. 自動觸發:經過配置 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size來完成

auto-aof-rewrite-percentage 100 :Redis會記住自從上一次重寫後AOF文件的大小(若是自Redis啓動後還沒重寫過,則記住啓動時使用的AOF文件的大小)。若是當前的文件大小比起記住的那個大小超過指定的百分比,則會觸配置發重寫。 

auto-aof-rewrite-min-size 64mb:同時須要設置一個文件大小最小值,只有大於這個值文件纔會重寫,以防文件很小,可是已經達到百分比的狀況。  

要禁用自動的日誌重寫功能,咱們能夠把百分比設置爲0:

auto-aof-rewrite-percentage 0: 禁用日誌重寫功能

5. 數據恢復 & Redis啓動加載數據

前面提到過,當AOF開啓時,Redis啓動時會優先載入AOF文件來恢復數據;

只有當AOF關閉時,纔會載入RDB文件恢復數據。

當AOF開啓,且AOF文件存在時,Redis啓動日誌:
image

持久化方案選擇

1. RDB和AOF的優缺點

RDB和AOF各有優缺點:

RDB持久化

  • 優勢:RDB文件緊湊,體積小,網絡傳輸快,適合全量複製;恢復速度比AOF快不少。固然,與AOF相比,RDB最重要的優勢之一是對性能的影響相對較小。
  • 缺點:RDB文件的致命缺點在於其數據快照的持久化方式決定了必然作不到實時持久化,而在數據愈來愈重要的今天,數據的大量丟失不少時候是沒法接受的,所以AOF持久化成爲主流。此外,RDB文件須要知足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

AOF持久化

  • 與RDB持久化相對應,AOF的優勢在於支持秒級持久化、兼容性好,缺點是文件大、恢復速度慢、對性能影響大。

2. 性能與實踐

經過上面的分析,咱們都知道RDB的快照、AOF的重寫都須要fork,這是一個重量級操做,會對Redis形成阻塞。所以爲了避免影響Redis主進程響應,咱們須要儘量下降阻塞。

  • 下降fork的頻率,好比能夠手動來觸發RDB生成快照、與AOF重寫;
  • 控制Redis最大使用內存,防止fork耗時過長;
  • 使用更牛逼的硬件;
  • 合理配置Linux的內存分配策略,避免由於物理內存不足致使fork失敗。

在線上咱們到底該怎麼作?我提供一些本身的實踐經驗。

  • 若是Redis中的數據並非特別敏感或者能夠經過其它方式重寫生成數據,能夠關閉持久化,若是丟失數據能夠經過其它途徑補回;
  • 本身制定策略按期檢查Redis的狀況,而後能夠手動觸發備份、重寫數據;
  • 單機若是部署多個實例,要防止多個機器同時運行持久化、重寫操做,防止出現內存、CPU、IO資源競爭,讓持久化變爲串行;
  • 能夠加入主從機器,利用一臺從機器進行備份處理,其它機器正常響應客戶端的命令;
  • RDB持久化與AOF持久化能夠同時存在,配合使用。

總結

Redis的高可用系列:持久化就已經講完了,持久化主要有RDB和AOF兩種技術,你們按照上面所介紹的原理和流程,根據線上具體需求選擇適合本身的持久化方案。

另外,寫原創技術文章不易,要花費好多時間和精力,但願你們看到文章也能有所收穫!大家的點贊和收藏就能成爲我繼續堅持輸出原創文章的動力!你們也能夠關注個人公衆號,訂閱更多個人文章!

歡迎關注公衆號:「 碼農富哥」,致力於分享後端技術 (高併發架構,分佈式集羣系統,消息隊列中間件,網絡,微服務,Linux, TCP/IP, HTTP, MySQL, Redis), Python 等 原創乾貨 和 面試指南!
關注公衆號後回覆【 資源】免費獲取 2T 編程視頻和電子書,回覆【 Redis】獲取 Redis高可用集羣架構視頻

image

相關文章
相關標籤/搜索