佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > 一篇文章揭秘Redis的磁盤持久化機(jī)制

一篇文章揭秘Redis的磁盤持久化機(jī)制

熱門標(biāo)簽:AI電銷 地方門戶網(wǎng)站 網(wǎng)站排名優(yōu)化 鐵路電話系統(tǒng) 百度競(jìng)價(jià)排名 呼叫中心市場(chǎng)需求 服務(wù)外包 Linux服務(wù)器

前言

Redis 是內(nèi)存數(shù)據(jù)庫(kù),數(shù)據(jù)都是存儲(chǔ)在內(nèi)存中,為了避免進(jìn)程退出導(dǎo)致數(shù)據(jù)的永久丟失,需要定期將 Redis 中的數(shù)據(jù)以數(shù)據(jù)或命令的形式從內(nèi)存保存到本地磁盤。當(dāng)下次 Redis 重啟時(shí),利用持久化文件進(jìn)行數(shù)據(jù)恢復(fù)。Redis 提供了 RDB 和 AOF 兩種持久化機(jī)制,前者將當(dāng)前的數(shù)據(jù)保存到磁盤,后者則是將每次執(zhí)行的寫命令保存到磁盤(類似于 MySQL 的 Binlog)。本文將詳細(xì)介紹 RDB 和 AOF 兩種持久化方案,包括操作方法和持久化的實(shí)現(xiàn)原理。

正文

Redis 是一個(gè)基于 K-V 存儲(chǔ)的數(shù)據(jù)庫(kù)服務(wù)器,下面先介紹 Redis 數(shù)據(jù)庫(kù)的內(nèi)部構(gòu)造以及 K-V 的存儲(chǔ)形式,有助于我們更容易理解 Redis 的持久化機(jī)制。

1. Redis數(shù)據(jù)庫(kù)結(jié)構(gòu)

一個(gè)單機(jī)的 Redis 服務(wù)器默認(rèn)情況下有 16 個(gè)數(shù)據(jù)庫(kù)(0-15號(hào)),默認(rèn)使用的是 0 號(hào)數(shù)據(jù)庫(kù),可以使用 SELECT 命令切換數(shù)據(jù)庫(kù)。


Redis 中的每個(gè)數(shù)據(jù)庫(kù)都由一個(gè) redis.h/redisDb 結(jié)構(gòu)表示,它記錄了單個(gè) Redis 數(shù)據(jù)庫(kù)的鍵空間、所有鍵的過期時(shí)間、處于阻塞狀態(tài)和就緒狀態(tài)的鍵、數(shù)據(jù)庫(kù)編號(hào)等等。

typedef struct redisDb {
 // 數(shù)據(jù)庫(kù)鍵空間,保存著數(shù)據(jù)庫(kù)中的所有鍵值對(duì)
 dict *dict;
 // 鍵的過期時(shí)間,字典的鍵為鍵,字典的值為過期事件 UNIX 時(shí)間戳
 dict *expires;
 // 正處于阻塞狀態(tài)的鍵
 dict *blocking_keys;
 // 可以解除阻塞的鍵
 dict *ready_keys;
 // 正在被 WATCH 命令監(jiān)視的鍵
 dict *watched_keys;
 struct evictionPoolEntry *eviction_pool;
 // 數(shù)據(jù)庫(kù)編號(hào)
 int id;
 // 數(shù)據(jù)庫(kù)的鍵的平均 TTL,統(tǒng)計(jì)信息
 long long avg_ttl;
} redisDb;

由于 Redis 是一個(gè)鍵值對(duì)數(shù)據(jù)庫(kù)(key-value pairs database), 所以它的數(shù)據(jù)庫(kù)本身也是一個(gè)字典,對(duì)應(yīng)的結(jié)構(gòu)正是 redisDb。其中,dict 指向的是一個(gè)記錄鍵值對(duì)數(shù)據(jù)的字典,它的鍵是一個(gè)字符串對(duì)象,它的值則可以是字符串、列表、哈希表、集合和有序集合在內(nèi)的任意一種 Redis 類型對(duì)象。 expires 指向的是一個(gè)用于記錄鍵的過期時(shí)間的字典,它的鍵為 dict 中的數(shù)據(jù)庫(kù)鍵,它的值為這個(gè)數(shù)據(jù)庫(kù)鍵的過期時(shí)間戳,這個(gè)值以 long long 類型表示。


2. RDB持久化

RDB 持久化(也稱作快照持久化)是指將內(nèi)存中的數(shù)據(jù)生成快照保存到磁盤里面,保存的文件后綴是 .rdb。rdb 文件是一個(gè)經(jīng)過壓縮的二進(jìn)制文件,當(dāng) Redis 重新啟動(dòng)時(shí),可以讀取 rdb 快照文件恢復(fù)數(shù)據(jù)。RDB 功能最核心的是 rdbSave 和 rdbLoad 兩個(gè)函數(shù), 前者用于生成 RDB 文件并保存到磁盤,而后者則用于將 RDB 文件中的數(shù)據(jù)重新載入到內(nèi)存中:


RDB 文件是一個(gè)單文件的全量數(shù)據(jù),很適合數(shù)據(jù)的容災(zāi)備份與恢復(fù),通過 RDB 文件恢復(fù)數(shù)據(jù)庫(kù)耗時(shí)較短,通常 1G 的快照文件載入內(nèi)存只需 20s 左右。Redis 提供了手動(dòng)觸發(fā)保存、自動(dòng)保存間隔兩種 RDB 文件的生成方式,下面先介紹 RDB 的創(chuàng)建和載入過程。

2.1. RDB的創(chuàng)建和載入

2.1.1. 手動(dòng)觸發(fā)保存

Redis 提供了兩個(gè)用于生成 RDB 文件的命令,一個(gè)是 SAVE,另一個(gè)是 BGSAVE。而觸發(fā) Redis 進(jìn)行 RDB 備份的方式有兩種,一種是通過 SAVE 命令、BGSAVE 命令手動(dòng)觸發(fā)快照生成的方式,另一種是配置保存時(shí)間和寫入次數(shù),由 Redis 根據(jù)條件自動(dòng)觸發(fā)保存操作。

1. SAVE命令

SAVE 是一個(gè)同步式的命令,它會(huì)阻塞 Redis 服務(wù)器進(jìn)程,直到 RDB 文件創(chuàng)建完成為止。在服務(wù)器進(jìn)程阻塞期間,服務(wù)器不能處理任何其他命令請(qǐng)求。

客戶端命令

127.0.0.1:6379> SAVE
OK

服務(wù)端日志

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

執(zhí)行 SAVE 命令后,Redis 在服務(wù)端進(jìn)程(PID為6266)執(zhí)行了 SAVE 操作,這個(gè)操作發(fā)生期間會(huì)一直阻塞 Redis 客戶端的請(qǐng)求處理。

2. BGSAVE命令

BGSAVE 是一個(gè)異步式的命令,和 SAVE 命令直接阻塞服務(wù)器進(jìn)程的做法不同,BGSAVE 命令會(huì)派生出一個(gè)子進(jìn)程,由子進(jìn)程負(fù)責(zé)創(chuàng)建 RDB 文件,服務(wù)器進(jìn)程(父進(jìn)程)繼續(xù)處理客戶的命令。

客戶端命令

127.0.0.1:6379> BGSAVE
Background saving started

服務(wù)端日志

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

通過服務(wù)端輸出的日志,可以發(fā)現(xiàn)Redis 在服務(wù)端進(jìn)程(PID為6266)會(huì)為 BGSAVE 命令單獨(dú)創(chuàng)建(fork)一個(gè)子進(jìn)程(PID為6283),并由子進(jìn)程在后臺(tái)完成 RDB 的保存過程,在操作完成之后通知父進(jìn)程然后退出。在整個(gè)過程中,服務(wù)器進(jìn)程只會(huì)消耗少量時(shí)間在創(chuàng)建子進(jìn)程和處理子進(jìn)程信號(hào)量上面,其余時(shí)間都是待命狀態(tài)。

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


  1. 客戶端發(fā)起 BGSAVE 命令,Redis 主進(jìn)程判斷當(dāng)前是否存在正在執(zhí)行備份的子進(jìn)程,如果存在則直接返回
  2. 父進(jìn)程 fork 一個(gè)子進(jìn)程 (fork 的過程中會(huì)造成阻塞的情況),這個(gè)過程可以使用 info stats 命令查看 latest_fork_usec 選項(xiàng),查看最近一次 fork 操作消耗的時(shí)間,單位是微秒
  3. 父進(jìn)程 fork 完成之后,則會(huì)返回 Background saving started 的信息提示,此時(shí) fork 阻塞解除
  4. fork 創(chuàng)建的子進(jìn)程開始根據(jù)父進(jìn)程的內(nèi)存數(shù)據(jù)生成臨時(shí)的快照文件,然后替換原文件
  5. 子進(jìn)程備份完畢后向父進(jìn)程發(fā)送完成信息,父進(jìn)程更新統(tǒng)計(jì)信息

3. SAVE和BGSAVE的比較

命令 SAVE BGSAVE
IO類型 同步 異步
是否阻塞 全程阻塞 fork時(shí)發(fā)生阻塞
復(fù)雜度 O(n) O(n)
優(yōu)點(diǎn) 不會(huì)消耗額外的內(nèi)存 不阻塞客戶端
缺點(diǎn) 阻塞客戶端 fork子進(jìn)程消耗內(nèi)存

2.1.2. 自動(dòng)觸發(fā)保存

因?yàn)?BGSAVE 命令可以在不阻塞服務(wù)器進(jìn)程的情況下執(zhí)行,所以 Redis 的配置文件 redis.conf 提供了一個(gè) save 選項(xiàng),讓服務(wù)器每隔一段時(shí)間自動(dòng)執(zhí)行一次 BGSAVE 命令。用戶可以通過 save 選項(xiàng)設(shè)置多個(gè)保存條件,只要其中任意一個(gè)條件被滿足,服務(wù)器就會(huì)執(zhí)行 BGSAVE 命令。 Redis 配置文件 redis.conf 默認(rèn)配置了以下 3 個(gè)保存條件:

save 900 1
save 300 10 
save 60 10000

那么只要滿足以下 3 個(gè)條件中的任意一個(gè),BGSAVE 命令就會(huì)被自動(dòng)執(zhí)行:

  • 服務(wù)器在 900 秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少 1 次修改。
  • 服務(wù)器在 300 秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少 10 次修改。
  • 服務(wù)器在 60 秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少 10000 次修改。

Redis 服務(wù)器會(huì)周期性地操作 serverCron 函數(shù),這個(gè)函數(shù)每隔 100 毫秒就會(huì)執(zhí)行一次,它的一項(xiàng)任務(wù)就是檢查 save 選項(xiàng)所設(shè)置的保存條件是否滿足,如果滿足的話,就自動(dòng)執(zhí)行 BGSAVE 命令。

2.1.3. 啟動(dòng)自動(dòng)載入

和使用 SAVE 和 BGSAVE 命令創(chuàng)建 RDB 文件不同,Redis 沒有專門提供用于載入 RDB 文件的命令,RDB 文件的載入過程是在 Redis 服務(wù)器啟動(dòng)時(shí)自動(dòng)完成的。啟動(dòng)時(shí)只要在指定目錄檢測(cè)到 RDB 文件的存在,Redis 就會(huì)通過 rdbLoad 函數(shù)自動(dòng)載入 RDB 文件。

下面是 Redis 服務(wù)器啟動(dòng)時(shí)打印的日志,倒數(shù)第 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 文件屬于全量的數(shù)據(jù)備份,所以更新頻率比 RDB 文件的更新頻率高。所以如果 Redis 服務(wù)器開啟了 AOF 持久化功能,那么服務(wù)器會(huì)優(yōu)先使用 AOF 文件來還原數(shù)據(jù)庫(kù)狀態(tài);只有在 AOF 的持久化功能處于關(guān)閉狀態(tài)時(shí),服務(wù)器才會(huì)使用優(yōu)先使用 RDB 文件還原數(shù)據(jù)庫(kù)狀態(tài)。


2.2. RDB的文件結(jié)構(gòu)

RDB 文件是經(jīng)過壓縮的二進(jìn)制文件,下面介紹關(guān)于RDB文件的一些細(xì)節(jié)。

2.2.1. 存儲(chǔ)路徑

SAVE 命令和 BGSAVE 命令都只會(huì)備份當(dāng)前數(shù)據(jù)庫(kù),備份文件名默認(rèn)為 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 文件的存儲(chǔ)路徑既可以在啟動(dòng)前配置,也可以通過命令動(dòng)態(tài)設(shè)定。

  • 配置項(xiàng):通過 dir 配置指定目錄,dbfilename 指定文件名
  • 動(dòng)態(tài)指定:Redis 啟動(dòng)后也可以動(dòng)態(tài)修改 RDB 存儲(chǔ)路徑,在磁盤損害或空間不足時(shí)非常有用,執(zhí)行命令為:
config set dir {newdir}
config set dbfilename {newFileName}

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

您可能感興趣的文章:
  • redis數(shù)據(jù)的兩種持久化方式對(duì)比
  • Redis做數(shù)據(jù)持久化的解決方案及底層原理
  • Redis教程(十):持久化詳解
  • Redis的持久化方案詳解
  • 淺談redis內(nèi)存數(shù)據(jù)的持久化方式
  • Redis數(shù)據(jù)持久化方式技術(shù)解析

標(biāo)簽:黃山 崇左 湘潭 衡水 湖南 蘭州 銅川 仙桃

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《一篇文章揭秘Redis的磁盤持久化機(jī)制》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    龙门县| 津南区| 湘阴县| 嘉禾县| 无棣县| 五指山市| 绥化市| 兴安盟| 腾冲县| 高青县| 沧州市| 托克逊县| 新蔡县| 广昌县| 社旗县| 股票| 龙海市| 得荣县| 北碚区| 炉霍县| 腾冲县| 涪陵区| 双流县| 台江县| 湾仔区| 调兵山市| 阿鲁科尔沁旗| 仁化县| 厦门市| 襄垣县| 拉孜县| 固原市| 武冈市| 侯马市| 昭觉县| 诸暨市| 日喀则市| 柘荣县| 武冈市| 特克斯县| 阜宁县|