一文帶你深刻了解 Redis 的持久化方式及其原理

Redis 提供了兩種持久化方式,一種是基於快照形式的 RDB,另外一種是基於日誌形式的 AOF,每種方式都有本身的優缺點,本文將介紹 Redis 這兩種持久化方式,但願閱讀本文後你對 Redis 的這兩種方式有更加全面、清晰的認識。java

RDB 快照方式持久化

先從 RDB 快照方式聊起,RDB 是 Redis 默認開啓的持久化方式,並不須要咱們單獨開啓,先來看看跟 RDB 相關的配置信息:linux

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#   save ""
# 自動生成快照的觸發機制 中間的是時間,單位秒,後面的是變動數據 60 秒變動 10000 條數據則自動生成快照
save 900 1
save 300 10
save 60 10000

# 生成快照失敗時,主線程是否中止寫入
stop-writes-on-bgsave-error yes

# 是否採用壓縮算法存儲
rdbcompression yes

# 數據恢復時是否檢測 RDB文件有效性
rdbchecksum yes

# The filename where to dump the DB
# RDB 快照生成的文件名稱
dbfilename dump.rdb

# 快照生成的路徑 AOF 也是存放在這個路徑下面
dir .
複製代碼

關於 RDB 相關配置信息很少,須要咱們調整的就更少了,咱們只須要根據本身的業務量修改生成快照的機制和文件存放路徑便可。redis

RDB 有兩種持久化方式:手動觸發自動觸發手動觸發使用如下兩個命令:算法

  • save:會阻塞當前 Redis 服務器響應其餘命令,直到 RDB 快照生成完成爲止,對於內存 比較大的實例會形成長時間阻塞,因此線上環境不建議使用緩存

  • bgsave:Redis 主進程會 fork 一個子進程,RDB 快照生成有子進程來負責,完成以後,子進程自動結束,bgsave 只會在 fork 子進程的時候短暫的阻塞,這個過程是很是短的,因此推薦使用該命令來手動觸發安全

除了執行命令手動觸發以外,Redis 內部還存在自動觸發 RDB 的持久化機制,在如下幾種狀況下 Redis 會自動觸發 RDB 持久化服務器

  • 在配置中配置了 save 相關配置信息,如咱們上面配置文件中的 save 60 10000 ,也能夠把它歸類爲「save m n」格式的配置,表示 m 秒內數據集存在 n 次修改時,會自動觸發 bgsave。微信

  • 在主從狀況下,若是從節點執行全量複製操做,主節點自動執行 bgsave 生成 RDB 文件併發送給從節點併發

  • 執行 debug reload 命令從新加載 Redis 時,也會自動觸發 save 操做app

  • 默認狀況下執行 shutdown 命令時,若是沒有開啓 AOF 持久化功能則自動執行 bgsave

上面就是 RDB 持久化的方式,能夠看出 save 命令使用的比較少,大多數狀況下使用的都是 bgsave 命令,因此這個 bgsave 命令仍是有一些東西,那接下來咱們就一塊兒看看 bgsave 背後的原理,先從流程圖開始入手:

bgsave 運做流程圖

bgsave 命令大概有如下幾個步驟:

  • 一、執行 bgsave 命令,Redis 主進程判斷當前是否存在正在執行的 RDB/AOF 子進程,若是存在, bgsave 命令直接返回不在往下執行。
  • 二、父進程執行 fork 操做建立子進程,fork 操做過程當中父進程會阻塞,fork 完成後父進程將不在阻塞能夠接受其餘命令。
  • 三、子進程建立新的 RDB 文件,基於父進程當前內存數據生成臨時快照文件,完成後用新的 RDB 文件替換原有的 RDB 文件,而且給父進程發送 RDB 快照生成完畢通知

上面就是 bgsave 命令背後的一些內容,RDB 的內容就差很少了,咱們一塊兒來總結 RDB 持久化的優缺點,RDB 方式的優勢

  • RDB 快照是某一時刻 Redis 節點內存數據,很是適合作備份,上傳到遠程服務器或者文件系統中,用於容災備份
  • 數據恢復時 RDB 要遠遠快於 AOF

有優勢一樣存在缺點,RDB 的缺點有

  • RDB 持久化方式數據沒辦法作到實時持久化/秒級持久化。咱們已經知道了 bgsave 命令每次運行都要執行 fork 操做建立子進程,屬於重量級操做,頻繁執行成本太高。
  • RDB 文件使用特定二進制格式保存,Redis 版本演進過程當中有多個格式 的 RDB 版本,存在老版本 Redis 服務沒法兼容新版 RDB 格式的問題

若是咱們對數據要求比較高,每一秒的數據都不能丟,RDB 持久化方式確定是不可以知足要求的,那 Redis 有沒有辦法知足呢,答案是有的,那就是接下來的 AOF 持久化方式

AOF 持久化方式

Redis 默認並無開啓 AOF 持久化方式,須要咱們自行開啓,在 redis.conf 配置文件中將 appendonly no 調整爲 appendonly yes,這樣就開啓了 AOF 持久化,與 RDB 不一樣的是 AOF 是以記錄操做命令的形式來持久化數據的,咱們能夠查看如下 AOF 的持久化文件 appendonly.aof

*2
$6
SELECT
$1
0
*3
$3
set
$6
mykey1
$6
你好
*3
$3
set
$4
key2
$5
hello
*1
$8
複製代碼

大概就是長這樣的,具體的你能夠查看你 Redis 服務器上的 appendonly.aof 配置文件,這也意味着咱們能夠在 appendonly.aof 文件中國修改值,等 Redis 重啓時將會加載修改以後的值。看似一些簡單的操做命令,其實從命令到 appendonly.aof 這個過程當中很是有學問的,下面時 AOF 持久化流程圖:

AOF 持久化流程圖

在 AOF 持久化過程當中有兩個很是重要的操做:一個是將操做命令追加到 AOF_BUF 緩存區,另外一個是 AOF_buf 緩存區數據同步到 AOF 文件,接下來咱們詳細聊一聊這兩個操做:

一、爲何要將命令寫入到 aof_buf 緩存區而不是直接寫入到 aof 文件?

咱們知道 Redis 是單線程響應,若是每次寫入 AOF 命令都直接追加到磁盤上的 AOF 文件中,這樣頻繁的 IO 開銷,Redis 的性能就完成取決於你的機器硬件了,爲了提高 Redis 的響應效率就添加了一層 aof_buf 緩存層, 利用的是操做系統的 cache 技術,這樣就提高了 Redis 的性能,雖然這樣性能是解決了,可是同時也引入了一個問題,aof_buf 緩存區數據如何同步到 AOF 文件呢?由誰同步呢?這就是咱們接下來要聊的一個操做:fsync 操做

二、aof_buf 緩存區數據如何同步到 aof 文件中?

aof_buf 緩存區數據寫入到 aof 文件是有 linux 系統去完成的,因爲 Linux 系統調度機制週期比較長,若是系統故障宕機了,意味着一個週期內的數據將所有丟失,這不是咱們想要的,因此 Linux 提供了一個 fsync 命令,fsync 是針對單個文件操做(好比這裏的 AOF 文件),作強制硬盤同步,fsync 將阻塞直到寫入硬盤完成後返回,保證了數據持久化,正是因爲有這個命令,因此 redis 提供了配置項讓咱們自行決定什麼時候進行磁盤同步,redis 在 redis.conf 中提供了appendfsync 配置項,有以下三個選項:

# appendfsync always
appendfsync everysec
# appendfsync no
複製代碼
  • always:每次有寫入命令都進行緩存區與磁盤數據同步,這樣保證不會有數據丟失,可是這樣會致使 redis 的吞吐量大大降低,降低到每秒只能支持幾百的 TPS ,這違背了 redis 的設計,因此不推薦使用這種方式
  • everysec:這是 redis 默認的同步機制,雖然每秒同步一次數據,看上去時間也很快的,可是它對 redis 的吞吐量沒有任何影響,每秒同步一次的話意味着最壞的狀況下咱們只會丟失 1 秒的數據, 推薦使用這種同步機制,兼顧性能和數據安全
  • no:不作任何處理,緩存區與 aof 文件同步交給系統去調度,操做系統同步調度的週期不固定,最長會有 30 秒的間隔,這樣出故障了就會丟失比較多的數據。

這就是三種磁盤同步策略,可是你有沒有注意到一個問題,AOF 文件都是追加的,隨着服務器的運行 AOF 文件會愈來愈大,體積過大的 AOF 文件對 redis 服務器甚至是主機都會有影響,並且在 Redis 重啓時加載過大的 AOF 文件須要過多的時間,這些都是不友好的,那 Redis 是如何解決這個問題的呢?Redis 引入了重寫機制來解決 AOF 文件過大的問題。

三、Redis 是如何進行 AOF 文件重寫的?

Redis AOF 文件重寫是把 Redis 進程內的數據轉化爲寫命令同步到新 AOF 文件的過程,重寫以後的 AOF 文件會比舊的 AOF 文件佔更小的體積,這是由如下幾個緣由致使的:

  • 進程內已經超時的數據再也不寫入文件
  • 舊的 AOF 文件含有無效命令,如 del key一、hdel key二、srem keys、set a1十一、set a222等。重寫使用進程內數據直接生成,這樣新的AOF文件只保 留最終數據的寫入命令
  • 多條寫命令能夠合併爲一個,如:lpush list a、lpush list b、lpush list c能夠轉化爲:lpush list a b c。爲了防止單條命令過大形成客戶端緩衝區溢 出,對於 list、set、hash、zset 等類型操做,以 64 個元素爲界拆分爲多條。

重寫以後的 AOF 文件體積更小了,不但可以節約磁盤空間,更重要的是在 Redis 數據恢復時,更小體積的 AOF 文件加載時間更短。AOF 文件重寫跟 RDB 持久化同樣分爲手動觸發自動觸發,手動觸發直接調用 bgrewriteaof 命令就行了,咱們後面會詳細聊一聊這個命令,自動觸發就須要咱們在 redis.conf 中修改如下幾個配置

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
複製代碼
  • auto-aof-rewrite-percentage:表明當前 AOF文件空間 (aof_current_size)和上一次重寫後 AOF 文件空間(aof_base_size)的比值,默認是 100%,也就是同樣大的時候
  • auto-aof-rewrite-min-size:表示運行 AOF 重寫時 AOF 文件最小體積,默認爲 64MB,也就是說 AOF 文件最小爲 64MB 纔有可能觸發重寫

知足了這兩個條件,Redis 就會自動觸發 AOF 文件重寫,AOF 文件重寫的細節跟 RDB 持久化生成快照有點相似,下面是 AOF 文件重寫流程圖:

AOF 文件重寫

AOF 文件重寫也是交給子進程來完成,跟 RDB 生成快照很像,AOF 文件重寫在重寫期間創建了一個 aof_rewrite_buf 緩存區來保存重寫期間主進程響應的命令,等新的 AOF 文件重寫完成後,將這部分文件同步到新的 AOF 文件中,最後用新的 AOF 文件替換掉舊的 AOF 文件。須要注意的是在重寫期間,舊的 AOF 文件依然會進行磁盤同步,這樣作的目的是防止重寫失敗致使數據丟失,

Redis 持久化數據恢復

咱們知道 Redis 是基於內存的,全部的數據都存放在內存中,因爲機器宕機或者其餘因素重啓了就會致使咱們的數據所有丟失,這也就是要作持久化的緣由,當服務器重啓時,Redis 會從持久化文件中加載數據,這樣咱們的數據就恢復到了重啓前的數據,在數據恢復這一塊Redis 是如何實現的?咱們先來看看數據恢復的流程圖:

Redis 數據恢復

Redis 的數據恢復流程比較簡單,優先恢復的是 AOF 文件,若是 AOF 文件不存在時則嘗試加載 RDB 文件,爲何 RDB 的恢復速度比 AOF 文件快,可是仍是會優先加載 AOF 文件呢?我我的認爲是 AOF 文件數據更全面而且 AOF 兼容性比 RDB 強,須要注意的是當存在 RDB/AOF 時,若是數據加載不成功,Redis 服務啓動會失敗。

最後

目前互聯網上不少大佬都有 Redis 系列教程,若有雷同,請多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,和平頭哥一塊兒學習,一塊兒進步。

平頭哥的技術博文
相關文章
相關標籤/搜索