1. 程式人生 > 資料庫 >一篇文章揭祕Redis的磁碟持久化機制

一篇文章揭祕Redis的磁碟持久化機制

前言

Redis 是記憶體資料庫,資料都是儲存在記憶體中,為了避免程序退出導致資料的永久丟失,需要定期將 Redis 中的資料以資料或命令的形式從記憶體儲存到本地磁碟。當下次 Redis 重啟時,利用持久化檔案進行資料恢復。Redis 提供了 RDB 和 AOF 兩種持久化機制,前者將當前的資料儲存到磁碟,後者則是將每次執行的寫命令儲存到磁碟(類似於 MySQL 的 Binlog)。本文將詳細介紹 RDB 和 AOF 兩種持久化方案,包括操作方法和持久化的實現原理。

正文

Redis 是一個基於 K-V 儲存的資料庫伺服器,下面先介紹 Redis 資料庫的內部構造以及 K-V 的儲存形式,有助於我們更容易理解 Redis 的持久化機制。

1. Redis資料庫結構

一個單機的 Redis 伺服器預設情況下有 16 個數據庫(0-15號),預設使用的是 0 號資料庫,可以使用 SELECT 命令切換資料庫。


Redis 中的每個資料庫都由一個 redis.h/redisDb 結構表示,它記錄了單個 Redis 資料庫的鍵空間、所有鍵的過期時間、處於阻塞狀態和就緒狀態的鍵、資料庫編號等等。

typedef struct redisDb {
 // 資料庫鍵空間,儲存著資料庫中的所有鍵值對
 dict *dict;
 // 鍵的過期時間,字典的鍵為鍵,字典的值為過期事件 UNIX 時間戳
 dict *expires;
 // 正處於阻塞狀態的鍵
 dict *blocking_keys;
 // 可以解除阻塞的鍵
 dict *ready_keys;
 // 正在被 WATCH 命令監視的鍵
 dict *watched_keys;
 struct evictionPoolEntry *eviction_pool;
 // 資料庫編號
 int id;
 // 資料庫的鍵的平均 TTL,統計資訊
 long long avg_ttl;
} redisDb;

由於 Redis 是一個鍵值對資料庫(key-value pairs database), 所以它的資料庫本身也是一個字典,對應的結構正是 redisDb。其中,dict 指向的是一個記錄鍵值對資料的字典,它的鍵是一個字串物件,它的值則可以是字串、列表、雜湊表、集合和有序集合在內的任意一種 Redis 型別物件。 expires 指向的是一個用於記錄鍵的過期時間的字典,它的鍵為 dict 中的資料庫鍵,它的值為這個資料庫鍵的過期時間戳,這個值以 long long 型別表示。


2. RDB持久化

RDB 持久化(也稱作快照持久化)是指將記憶體中的資料生成快照儲存到磁盤裡面,儲存的檔案字尾是 .rdb。rdb 檔案是一個經過壓縮的二進位制檔案,當 Redis 重新啟動時,可以讀取 rdb 快照檔案恢復資料。RDB 功能最核心的是 rdbSave 和 rdbLoad 兩個函式, 前者用於生成 RDB 檔案並儲存到磁碟,而後者則用於將 RDB 檔案中的資料重新載入到記憶體中:


RDB 檔案是一個單檔案的全量資料,很適合資料的容災備份與恢復,通過 RDB 檔案恢復資料庫耗時較短,通常 1G 的快照檔案載入記憶體只需 20s 左右。Redis 提供了手動觸發儲存、自動儲存間隔兩種 RDB 檔案的生成方式,下面先介紹 RDB 的建立和載入過程。

2.1. RDB的建立和載入

2.1.1. 手動觸發儲存

Redis 提供了兩個用於生成 RDB 檔案的命令,一個是 SAVE,另一個是 BGSAVE。而觸發 Redis 進行 RDB 備份的方式有兩種,一種是通過 SAVE 命令、BGSAVE 命令手動觸發快照生成的方式,另一種是配置儲存時間和寫入次數,由 Redis 根據條件自動觸發儲存操作。

1. SAVE命令

SAVE 是一個同步式的命令,它會阻塞 Redis 伺服器程序,直到 RDB 檔案建立完成為止。在伺服器程序阻塞期間,伺服器不能處理任何其他命令請求。

客戶端命令

127.0.0.1:6379> SAVE
OK

服務端日誌

6266:M 15 Sep 2019 08:31:01.258 * DB saved on disk

執行 SAVE 命令後,Redis 在服務端程序(PID為6266)執行了 SAVE 操作,這個操作發生期間會一直阻塞 Redis 客戶端的請求處理。

2. BGSAVE命令

BGSAVE 是一個非同步式的命令,和 SAVE 命令直接阻塞伺服器程序的做法不同,BGSAVE 命令會派生出一個子程序,由子程序負責建立 RDB 檔案,伺服器程序(父程序)繼續處理客戶的命令。

客戶端命令

127.0.0.1:6379> BGSAVE
Background saving started

服務端日誌

6266:M 15 Sep 2019 08:31:22.914 * Background saving started by pid 6283
6283:C 15 Sep 2019 08:31:22.915 * DB saved on disk
6266:M 15 Sep 2019 08:31:22.934 * Background saving terminated with success

通過服務端輸出的日誌,可以發現Redis 在服務端程序(PID為6266)會為 BGSAVE 命令單獨建立(fork)一個子程序(PID為6283),並由子程序在後臺完成 RDB 的儲存過程,在操作完成之後通知父程序然後退出。在整個過程中,伺服器程序只會消耗少量時間在建立子程序和處理子程序訊號量上面,其餘時間都是待命狀態。

BGSAVE 是觸發 RDB 持久化的主流方式,下面給出 BGSAVE 命令生成快照的流程:


  1. 客戶端發起 BGSAVE 命令,Redis 主程序判斷當前是否存在正在執行備份的子程序,如果存在則直接返回
  2. 父程序 fork 一個子程序 (fork 的過程中會造成阻塞的情況),這個過程可以使用 info stats 命令檢視 latest_fork_usec 選項,檢視最近一次 fork 操作消耗的時間,單位是微秒
  3. 父程序 fork 完成之後,則會返回 Background saving started 的資訊提示,此時 fork 阻塞解除
  4. fork 建立的子程序開始根據父程序的記憶體資料生成臨時的快照檔案,然後替換原檔案
  5. 子程序備份完畢後向父程序傳送完成資訊,父程序更新統計資訊

3. SAVE和BGSAVE的比較

命令 SAVE BGSAVE
IO型別 同步 非同步
是否阻塞 全程阻塞 fork時發生阻塞
複雜度 O(n) O(n)
優點 不會消耗額外的記憶體 不阻塞客戶端
缺點 阻塞客戶端 fork子程序消耗記憶體

2.1.2. 自動觸發儲存

因為 BGSAVE 命令可以在不阻塞伺服器程序的情況下執行,所以 Redis 的配置檔案 redis.conf 提供了一個 save 選項,讓伺服器每隔一段時間自動執行一次 BGSAVE 命令。使用者可以通過 save 選項設定多個儲存條件,只要其中任意一個條件被滿足,伺服器就會執行 BGSAVE 命令。 Redis 配置檔案 redis.conf 預設配置了以下 3 個儲存條件:

save 900 1
save 300 10 
save 60 10000

那麼只要滿足以下 3 個條件中的任意一個,BGSAVE 命令就會被自動執行:

  • 伺服器在 900 秒之內,對資料庫進行了至少 1 次修改。
  • 伺服器在 300 秒之內,對資料庫進行了至少 10 次修改。
  • 伺服器在 60 秒之內,對資料庫進行了至少 10000 次修改。

Redis 伺服器會週期性地操作 serverCron 函式,這個函式每隔 100 毫秒就會執行一次,它的一項任務就是檢查 save 選項所設定的儲存條件是否滿足,如果滿足的話,就自動執行 BGSAVE 命令。

2.1.3. 啟動自動載入

和使用 SAVE 和 BGSAVE 命令建立 RDB 檔案不同,Redis 沒有專門提供用於載入 RDB 檔案的命令,RDB 檔案的載入過程是在 Redis 伺服器啟動時自動完成的。啟動時只要在指定目錄檢測到 RDB 檔案的存在,Redis 就會通過 rdbLoad 函式自動載入 RDB 檔案。

下面是 Redis 伺服器啟動時列印的日誌,倒數第 2 條日誌是在成功載入 RDB 檔案後列印的。

$ redis-server /usr/local/etc/redis.conf
6266:C 15 Sep 2019 08:30:41.830 # Redis version=5.0.5,bits=64,commit=00000000,modified=0,pid=6266,just started
6266:C 15 Sep 2019 08:30:41.830 # Configuration loaded
6266:M 15 Sep 2019 08:30:41.831 * Increased maximum number of open files to 10032 (it was originally set to 256).
6266:M 15 Sep 2019 08:30:41.832 # Server initialized
6266:M 15 Sep 2019 08:30:41.833 * DB loaded from disk: 0.001 seconds
6266:M 15 Sep 2019 08:30:41.833 * Ready to accept connections

由於 AOF 檔案屬於增量的寫入命令備份,RDB 檔案屬於全量的資料備份,所以更新頻率比 RDB 檔案的更新頻率高。所以如果 Redis 伺服器開啟了 AOF 持久化功能,那麼伺服器會優先使用 AOF 檔案來還原資料庫狀態;只有在 AOF 的持久化功能處於關閉狀態時,伺服器才會使用優先使用 RDB 檔案還原資料庫狀態。


2.2. RDB的檔案結構

RDB 檔案是經過壓縮的二進位制檔案,下面介紹關於RDB檔案的一些細節。

2.2.1. 儲存路徑

SAVE 命令和 BGSAVE 命令都只會備份當前資料庫,備份檔名預設為 dump.rdb,可通過配置檔案修改備份檔名 dbfilename xxx.rdb。可以通過以下命令檢視備份檔案目錄和 RDB 檔名稱:

$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/var/db/redis"
127.0.0.1:6379> CONFIG GET dbfilename
1) "dbfilename"
2) "dump.rdb"

RDB 檔案的儲存路徑既可以在啟動前配置,也可以通過命令動態設定。

  • 配置項:通過 dir 配置指定目錄,dbfilename 指定檔名
  • 動態指定:Redis 啟動後也可以動態修改 RDB 儲存路徑,在磁碟損害或空間不足時非常有用,執行命令為:
config set dir {newdir}
config set dbfilename {newFileName}

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。