Redis持久化錦囊在手,再也不會(huì)擔(dān)心數(shù)據(jù)丟失了
Redis 的讀寫(xiě)都是在內(nèi)存中進(jìn)行的,所以它的性能高。而當(dāng)我們的服務(wù)器斷開(kāi)或者重啟的時(shí)候,數(shù)據(jù)就會(huì)消失,那么我們?cè)撛趺唇鉀Q這個(gè)問(wèn)題呢?
其實(shí) Redis 已經(jīng)為我們提供了一種持久化的機(jī)制,分別是 RDB 和 AOF 兩種方式,接下來(lái)跟著我一起看看這兩個(gè)錦囊都是怎么保證數(shù)據(jù)的持久化的。
持久化
由于 Redis 是基于內(nèi)存的數(shù)據(jù)庫(kù),所以當(dāng)服務(wù)器出現(xiàn)故障的時(shí)候,我們的數(shù)據(jù)就得不到安全保障。
這個(gè)時(shí)候就需要將內(nèi)存中的數(shù)據(jù)存儲(chǔ)到磁盤(pán)中,當(dāng)我們服務(wù)器重啟時(shí),便可以通過(guò)磁盤(pán)來(lái)恢復(fù)數(shù)據(jù),這個(gè)過(guò)程就叫做 Redis 持久化。
Redis持久化
RDB
簡(jiǎn)介
RDB全稱(chēng)Redis Database Backup file(Redis數(shù)據(jù)備份文件),也可以稱(chēng)為Redis數(shù)據(jù)快照。
- RDB 文件是一個(gè)經(jīng)過(guò)壓縮的二進(jìn)制文件(默認(rèn):dump.rdb);
- RDB 文件保存在硬盤(pán)里;
- 通過(guò)保存數(shù)據(jù)庫(kù)中的鍵值對(duì)來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)。
創(chuàng)建
當(dāng) Redis 持久化時(shí),程序會(huì)將當(dāng)前內(nèi)存中的數(shù)據(jù)庫(kù)狀態(tài)保存到磁盤(pán)中。
創(chuàng)建
創(chuàng)建 RDB 文件主要有兩個(gè) Redis 命令:SAVE 和 BGSAVE。
SAVE
同步操作,執(zhí)行命令時(shí),會(huì)阻塞 Redis 服務(wù)器進(jìn)程,拒絕客戶(hù)端發(fā)送的命令請(qǐng)求。
代碼示例:
- def SAVE():
- # 創(chuàng)建 RDB 文件
- rdbSave()
圖示:
Save命令
BGSAVE
異步操作,執(zhí)行命令時(shí),子進(jìn)程執(zhí)行保存工作,服務(wù)器還可以繼續(xù)讓主線(xiàn)程處理客戶(hù)端發(fā)送的命令請(qǐng)求。
代碼示例:
- def BGSAVE():
- # 創(chuàng)建子進(jìn)程
- pid = fork()
- if pid == 0:
- # 子進(jìn)程負(fù)責(zé)創(chuàng)建 RDB 文件
- rdbSave()
- # 完成之后向父進(jìn)程發(fā)送信號(hào)
- signal_parent()
- elif pid > 0:
- # 父進(jìn)程繼續(xù)處理命令請(qǐng)求,并通過(guò)輪訓(xùn)等待子進(jìn)程的信號(hào)
- handle_request_and_wait_signal()
- else:
- handle_fork_error()
圖示:
bgSave命令
載入
載入工作在服務(wù)器啟動(dòng)時(shí)自動(dòng)執(zhí)行。
載入
服務(wù)器在載入 RDB 文件期間,會(huì)一直處于阻塞狀態(tài),直到載入工作完成為止。
主要設(shè)置
Redis 允許用戶(hù)通過(guò)設(shè)置服務(wù)器配置的 save 選項(xiàng),讓服務(wù)器每隔一段時(shí)間自動(dòng)執(zhí)行一次 BGSAVE 命令。
設(shè)置保存條件
提供配置如下:
- save 900 1
- save 300 10
在這種情況下,只要滿(mǎn)足以下條件中的一個(gè),BGSAVE 命令就會(huì)被執(zhí)行:
- 服務(wù)器在 900 秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少 1 次修改了;
- 服務(wù)器在 300 秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少 10 次修改。
saveparams
服務(wù)器程序會(huì)根據(jù) save 選項(xiàng)所設(shè)置的保存條件,設(shè)置服務(wù)器狀態(tài) redisServer 結(jié)構(gòu)的 saveparams 屬性。
- saveparams 屬性是一個(gè)數(shù)組;
- 數(shù)組中的每一個(gè)元素都是一個(gè) saveparam 結(jié)構(gòu);
- 每個(gè) saveparam 結(jié)構(gòu)都保存了一個(gè) save 選項(xiàng)設(shè)置的保存條件。
- struct saveparam {
- // 秒數(shù)
- time_t seconds;
- // 修改數(shù)
- int changes;
- }
dirty
dirty 計(jì)數(shù)器記錄距離上一次成功執(zhí)行 SAVE 命令或 BGSAVE 命令之后,服務(wù)器對(duì)數(shù)據(jù)庫(kù)狀態(tài)進(jìn)行了多少次修改(包括寫(xiě)入、刪除、更新等操作)。
lastsave
是一個(gè) UNINX 時(shí)間戳,記錄了服務(wù)器上一次成功執(zhí)行 SAVE 命令或者 BGSAVE 命令的時(shí)間。
檢查保存條件是否滿(mǎn)足
服務(wù)器周期性操作函數(shù) serverCron (該函數(shù)對(duì)正在運(yùn)行的服務(wù)器進(jìn)行維護(hù))默認(rèn)每隔 100 毫秒就會(huì)執(zhí)行一次,其中一項(xiàng)工作就是檢查 save 選項(xiàng)所設(shè)置的保存條件是否已經(jīng)滿(mǎn)足,滿(mǎn)足的話(huà)就執(zhí)行 BGSAVE 命令。
代碼示例:
- def serverCron():
- # ....
- # 遍歷所有保存條件
- for saveparam in server.saveparams:
- # 計(jì)算距離上次執(zhí)行保存操作有多少秒
- save_interval = unixtime_now() - server.lastsave
- # 如果數(shù)據(jù)庫(kù)狀態(tài)的修改次數(shù)超過(guò)條件所設(shè)置的次數(shù)
- # 如果距離上次保存的時(shí)間超過(guò)條件所設(shè)置的時(shí)間
- if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
- BGSAVE()
默認(rèn)配置
RDB 文件默認(rèn)的配置如下:
- ################################ SNAPSHOTTING ################################
- #
- # Save the DB on disk:
- #在給定的秒數(shù)和給定的對(duì)數(shù)據(jù)庫(kù)的寫(xiě)操作數(shù)下,自動(dòng)持久化操作。
- # save <seconds> <changes>
- #
- save 900 1
- save 300 10
- save 60 10000
- #bgsave發(fā)生錯(cuò)誤時(shí)是否停止寫(xiě)入,一般為yes
- stop-writes-on-bgsave-error yes
- #持久化時(shí)是否使用LZF壓縮字符串對(duì)象?
- rdbcompression yes
- #是否對(duì)rdb文件進(jìn)行校驗(yàn)和檢驗(yàn),通常為yes
- rdbchecksum yes
- # RDB持久化文件名
- dbfilename dump.rdb
- #持久化文件存儲(chǔ)目錄
- dir ./
AOF
簡(jiǎn)介
AOF全稱(chēng)為 Append Only File(追加日志文件)。日志是寫(xiě)后日志,Redis 是先執(zhí)行命令,把數(shù)據(jù)寫(xiě)入內(nèi)存,然后才記錄日志。
寫(xiě)后日志
- 通過(guò)保存 Redis 服務(wù)器所執(zhí)行的寫(xiě)命令來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài);
- 寫(xiě)入 AOF 文件的所有命令都是以 Redis 的命令請(qǐng)求協(xié)議格式保存的。
實(shí)現(xiàn)
AOF 持久化流程實(shí)現(xiàn)主要是通過(guò)以下流程來(lái)實(shí)現(xiàn)的:
AOF流程
命令追加
若 AOF 持久化功能處于打開(kāi)狀態(tài),服務(wù)器在執(zhí)行完一個(gè)命令后,會(huì)以協(xié)議格式將被執(zhí)行的寫(xiě)命令追加到服務(wù)器狀態(tài)的 aof_buf 緩沖區(qū)的末尾。
文件同步
服務(wù)器每次結(jié)束一個(gè)事件循環(huán)之前,都會(huì)調(diào)用 flushAppendOnlyFile 函數(shù),這個(gè)函數(shù)會(huì)考慮是否需要將 aof_buf 緩沖區(qū)中的內(nèi)容寫(xiě)入和保存到 AOF 文件里。
flushAppendOnlyFile 函數(shù)執(zhí)行以下流程:
- WRITE:根據(jù)條件,將 aof_buf 中的緩存寫(xiě)入到 AOF 文件;
- SAVE:根據(jù)條件,調(diào)用 fsync 或 fdatasync 函數(shù),將 AOF 文件保存到磁盤(pán)中。
這個(gè)函數(shù)是由服務(wù)器配置的 appendfsync 的三個(gè)值:always、everysec、no來(lái)影響的,也被稱(chēng)為三種策略。
Always
每條命令都會(huì) fsync 到硬盤(pán)中,這樣 redis 的寫(xiě)入數(shù)據(jù)就不會(huì)丟失。
Always
everysec
每秒都會(huì)刷新緩沖區(qū)到硬盤(pán)中(默認(rèn)值)。
everysec
no
根據(jù)當(dāng)前操作系統(tǒng)的規(guī)則決定什么時(shí)候刷新到硬盤(pán)中,不需要我們來(lái)考慮。
no
數(shù)據(jù)加載
- 創(chuàng)建一個(gè)不帶網(wǎng)絡(luò)連接的偽客戶(hù)端;
- 從 AOF 文件中分析并讀取出一條寫(xiě)命令;
- 使用偽客戶(hù)端執(zhí)行被讀出的寫(xiě)命令;
- 一直執(zhí)行步驟 2 和 3,直到 AOF 文件中的所有寫(xiě)命令都被處理完畢為止。
文件重寫(xiě)
為何需要文件重寫(xiě):
- 為了解決 AOF 文件體積膨脹的問(wèn)題;
- 通過(guò)重寫(xiě)創(chuàng)建一個(gè)新的 AOF 文件來(lái)替代現(xiàn)有的 AOF 文件,新的 AOF 文件不會(huì)包含任何浪費(fèi)空間的冗余命令。
實(shí)現(xiàn)
文件重寫(xiě)的實(shí)現(xiàn)原理:
- 不需要對(duì)現(xiàn)有的 AOF 文件進(jìn)行任何操作;
- 從數(shù)據(jù)庫(kù)中直接讀取鍵現(xiàn)在的值;
- 用一條命令記錄鍵值對(duì),從而代替之前記錄這個(gè)鍵值對(duì)的多條命令。
后臺(tái)重寫(xiě)
為不阻塞父進(jìn)程,Redis 將 AOF 重寫(xiě)程序放到子進(jìn)程里執(zhí)行。
在子進(jìn)程執(zhí)行 AOF 重寫(xiě)期間,服務(wù)器進(jìn)程需要執(zhí)行三個(gè)流程:
- 執(zhí)行客戶(hù)端發(fā)來(lái)的命令;
- 將執(zhí)行后的寫(xiě)命令追加到 AOF 緩沖區(qū);
- 將執(zhí)行后的寫(xiě)命令追加到 AOF 重寫(xiě)緩沖區(qū)。
服務(wù)器流程
默認(rèn)配置
AOF 文件默認(rèn)的配置如下:
- ############################## APPEND ONLY MODE ###############################
- #開(kāi)啟AOF持久化方式
- appendonly no
- #AOF持久化文件名
- appendfilename "appendonly.aof"
- #每秒把緩沖區(qū)的數(shù)據(jù)fsync到磁盤(pán)
- appendfsync everysec
- # appendfsync no
- #是否在執(zhí)行重寫(xiě)時(shí)不同步數(shù)據(jù)到AOF文件
- no-appendfsync-on-rewrite no
- # 觸發(fā)AOF文件執(zhí)行重寫(xiě)的增長(zhǎng)率
- auto-aof-rewrite-percentage 100
- #觸發(fā)AOF文件執(zhí)行重寫(xiě)的最小size
- auto-aof-rewrite-min-size 64mb
- #redis在恢復(fù)時(shí),會(huì)忽略最后一條可能存在問(wèn)題的指令
- aof-load-truncated yes
- #是否打開(kāi)混合開(kāi)關(guān)
- aof-use-rdb-preamble yes
總結(jié)
通過(guò)以上的簡(jiǎn)介,想必大家都對(duì) Redis 持久化有了大致的了解,那么這兩種方式,我們?cè)撊绾芜x擇呢?
- 對(duì)于大中型的應(yīng)用,我們既想保證數(shù)據(jù)完整性又想保證高效率,就應(yīng)該結(jié)合使用 RDB 和 AOF 兩種方式;
- 如果只是需要保證數(shù)據(jù)的完整性,保護(hù)數(shù)據(jù)不會(huì)丟失,那么優(yōu)先使用 AOF 方式;
- 如果是處理大規(guī)模的數(shù)據(jù)恢復(fù),追求更高更快的效率的話(huà),優(yōu)先使用 RDB 方式。
也可以參照下圖進(jìn)行選擇:

主要對(duì)比
本文轉(zhuǎn)載自微信公眾號(hào)「淺羽的IT小屋」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系淺羽的IT小屋公眾號(hào)。