1. 概述
我們知道 Redis 是一個記憶體資料庫,也就意味著如果我們的電腦例外重啟或者服務器宕機的情況下,存盤在 Redis 中的資料會丟失,
Redis 雖然是個記憶體資料庫,但是 Redis 支持 RDB 和 AOF 兩種持久化機制,將資料寫往磁盤,可以有效地避免因行程退出造成的資料丟失問題,當下次重啟時利用之前持久化的檔案即可實作資料恢復,
2. RDB
RDB(Redis DataBase) 持久化是把當前行程資料生成快照保存到硬碟的程序,什么是快照?你可以理解成把當前時刻的資料拍成一張照片保存下來,
RDB持久化是指在指定的時間間隔內將記憶體中的資料集快照寫入磁盤,也是默認的持久化方式,這種方式是就是將記憶體中資料以快照的方式寫入到二進制檔案中,默認的檔案名為dump.rdb,
前面說了,RDB 是快照的方式來保存資料的,因此對于 RDB 來說,提供了兩種觸發機制, 手動觸發和自動觸發,
手動觸發分別對應 save 和 bgsave 命令,
2.1 save
save 命令會阻塞當前 Redis 服務器,直到 RDB 程序完成為止,

因此,對于記憶體比較大的實體會造成長時間阻塞,線上環境不建議使用,
2.2 bgsave
bgsave 命令執行時,Redis 行程執行 fork 操作創建子行程,RDB 持久化程序由子行程負責,完成后自動結束,阻塞只發生在 fork 階段,一般時間很短,

顯然 bgsave 命令是針對 save 阻塞問題做的優化,因此 Redis 內部所有的涉及 RDB 的操作都采用 bgsave 的方式,其具體流程如下:
-
執行
bgsave命令,Redis 父行程判斷當前是否存在正在執行的子行程,如 RDB/AOF 子行程,如果存在,bgsave 命令直接回傳, -
父行程執行 fork 操作創建子行程,fork 操作程序中父行程會阻塞,通過
info stats命令查看latest_fork_usec選項,可以獲取最近一個 fork 操作的耗時,單位為微秒,
-
父行程 fork 完成后,
bgsave命令回傳 **Background saving started **資訊并不再阻塞父行程,可以繼續回應其他命令, -
子行程創建 RDB 檔案,根據父行程記憶體生成臨時快照檔案,完成后對原有檔案進行原子替換,執行 lastsave 命令可以獲取最后一次生成 RDB 的時間,對應 info 統計的 rdb_last_save_time 選項,
127.0.0.1:6379> lastsave (integer) 1640574329 -
行程發送信號給父行程表示完成,父行程更新統計資訊,
2.3 自動觸發
除了執行命令手動觸發之外,Redis 內部還存在自動觸發 RDB 的持久化機制,自動觸發是由我們的組態檔來完成的,在redis.conf組態檔中,有如下配置:
-
savesave m n,指在 m 秒內,如果有 n 個鍵發生改變,則自動觸發持久化(執行一次 bgsave 命令),例如,save 60 1 則表明在 60 秒內,至少有一個鍵發生改變,就會觸發 RDB 持久化,
-
stop-writes-on-bgsave-error默認值為 yes,當啟用了 RDB 且最后一次后臺保存資料失敗,Redis 是否停止接收資料,這會讓用戶意識到資料沒有正確持久化到磁盤上,否則沒有人會注意到災難(disaster)發生了,如果Redis重啟了,那么又可以重新開始接收資料了,
-
rdbcompression默認值是 yes,對于存盤到磁盤中的快照,可以設定是否進行壓縮存盤,
-
dbfilename設定快照的檔案名,默認是 dump.rdb,
-
dir設定快照檔案的存放路徑,這個配置項一定是個目錄,而不能是檔案名,默認為
./,
2.4 RDB 檔案
RDB 生成的持久化檔案默認為dump.rdb,可在組態檔中設定,也可以通過以下命令查找:
find / -name dump.rdb
# linux 下查找
[root@localhost ~]# find / -name dump.rdb
/usr/local/redis/redis-6.2.6/src/dump.rdb
RDB 檔案保存在 dir 配置指定的目錄下,檔案名通過 dbfilename 配置指定,
可以通過執行 config set dir {newDir}和 config set dbfilename {newFileName}運行期動態執行,當下次運行時 RDB 檔案會保存到新目錄,
Redis 默認采用 LZF 壓縮演算法對生成的 RDB 檔案做壓縮處理,壓縮后的檔案遠遠小于記憶體大小,默認開啟,可以通過引數 config set rdbcompression {yes|no}動態修改,
雖然壓縮 RDB 會消耗 CPU,但可大幅降低檔案的體積,方便保存到硬碟或通過網維示絡發送給從節點,因此線上建議開啟,
如果 Redis 加載損壞的 RDB 檔案時拒絕啟動,并列印如下日志:
# Short read or 0OM loading DB. Unrecoverable error,aborting now.
這時可以使用 Redis 提供的 redis-check-dump 工具檢測 RDB 檔案并獲取對應的錯誤報告,
2.5 RDB 的優缺點
2.5.1 優點
- RDB 是一個緊湊壓縮的二進制檔案,代表 Redis 在某個時間點上的資料快照,非常適用于備份,全量復制等場景, 比如每隔幾小時執行
bgsave備份,并把 RDB 檔案拷貝到遠程機器或者檔案系統中(如 hdfs),用于災難恢復, - 生成 RDB 檔案的時候,Redis 主行程會 fork 一個子行程來處理所有保存作業,主行程不需要進行任何磁盤 IO操作,
- Redis 加載 RDB 恢復資料遠遠快于 AOF 的方式,
2.5.2 缺點
- RDB 方式資料沒辦法做到實時持久化/秒級持久化,因為
bgsave每次運行都要執行 fork 操作創建子行程,屬于重量級操作,頻繁執行成本過高, - RDB 檔案使用特定二進制格式保存,Redis 版本演程序序中有多個格式的 RDB 版本,存在老版本 Redis 服務無法兼容新版 RDB 格式的問題,
針對 RDB 不適合實時持久化的問題,Redis 提供了 AOF 持久化方式來解決,
3. AOF
AOF(Append Only File)持久化,以獨立日志的方式記錄每次寫命令,重啟時再重新執行 AOF 檔案中的命令達到恢復資料的目的,
AOF 的主要作用是解決了資料持久化的實時性,目前已經是 Redis 持久化的主流方式,相比 RDB 的全量備份節省了很多時間,
開啟 AOF 功能需要設定配置,默認不開啟,AOF 檔案名通過 appendfilename 配置設定,默認檔案名是 appendonly.aof,保存路徑同 RDB 持久化方式一致,通過 dir 配置指定,
# 開啟 AOF
appendonly yes
# AOF 檔案名
appendfilename "appendonly.aof"
# always 每收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
# everysec 每秒強制寫入磁盤一次,性能和持久化方面做了折中,推薦
appendfsync everysec
# 正在匯出rdb快照的程序中,要不要停止同步aof
no-appendfsync-on-rewrite yes
# aof檔案大小比起上次重寫時的大小,增長率100%時,重寫
auto-aof-rewrite-percentage 100
# aof檔案,至少超過64M時,重寫
auto-aof-rewrite-min-size 64mb
3.1 作業流程
AOF 的作業流程如下:
-
命令寫入(append)
所有的寫入命令會追加到 aof_buf(緩沖區)中,
-
檔案同步(sync)
AOF 緩沖區根據對應的策略向硬碟做同步操作,
-
檔案重寫(rewrite)
隨著 AOF 檔案越來越大,需要定期對 AOF 檔案進行重寫,達到壓縮的目的,
-
重啟加載(load)
當 Redis 服務器重啟時,可以加載 AOF 檔案進行資料恢復,

3.2 命令寫入
AOF 命令寫入的內容直接是 RESP 文本協議格式,例如 set hello world 這條命令,在 AOF 緩沖區會追加如下文本:
* 3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
AOF 為什么直接采用文本協議格式?
文本協議具有很好的兼容性,開啟 AOF 后,所有寫入命令都包含追加操作,直接采用協議格式,避免了二次處理開銷,文本協議具有可讀性,方便直接修改和處理,
AOF 為什么把命令追加到 aof_buf 中?
Redis 使用單執行緒回應命令,如果每次寫 AOF 檔案命令都直接追加到硬碟,那么性能完全取決于當前硬碟負載,先寫入緩沖區 aof_buf 中,還有另一個好處,Redis 可以提供多種緩沖區同步硬碟的策略,在性能和安全性方面做出平衡,

3.3 檔案同步
Redis 提供了多種 AOF 緩沖區同步檔案策略,由引數 appendfsync 控制,主要有以下三種:
-
always
命令寫入 aof_buf 后呼叫系統 fsync 操作同步到 AOF 檔案,fsync 完成后執行緒回傳命令 fsync 同步檔案,
配置為 always 時,每次寫入都要同步 AOF 檔案,在一般的 SATA 硬碟上,Redis 只能支持大約幾百 TPS 寫入,顯然跟 Redis 高性能特性背道而馳,不建議配置,
-
everysec
寫人 aof_buf 后呼叫系統 write 操作,write 完成后執行緒回傳,操作由專門執行緒每秒呼叫一次 fsync 命令,
配置為 everysec,是建議的同步策略,也是默認配置,做到兼顧性能和資料安全性,理論上只有在系統突然宕機的情況下丟失 1 秒的資料,
-
no
寫入 aof_buf 后呼叫系統 write 操作,不對 AOF 檔案做 fsync 同步,同步硬碟操作由作業系統負責,通常同步周期最長 30 秒,
配置為 no,由于作業系統每次同步 AOF 檔案的周期不可控,而且會加大每次同步硬碟的資料量,雖然提升了性能,但資料安全性無法保證,
系統呼叫 write 和 fsync
write
操作會觸發延遲寫(delayed write)機制,Linux 在內核提供頁緩沖區用來提高硬碟 IO 性能,write 操作在寫入系統緩沖區后直接回傳,同步硬碟操作依賴于系統調度機制,例如:緩沖區頁空間寫滿或達到特定時間周期,同步檔案之前,如果此時系統故障宕機,緩沖區內資料將丟失,
fsync
針對單個檔案操作(比如 AOF 檔案),做強制硬碟同步,fsync 將阻塞直到寫入硬碟完成后回傳,保證了資料持久化,
3.4 檔案重寫
隨著命令不斷寫入 AOF,檔案會越來越大,為了解決這個問題,Redis 引入 AOF 重寫機制壓縮檔案體積,AOF 檔案重寫是把 Redis 行程內的資料轉化為寫命令同步到新 AOF 檔案的程序,

重寫后的 AOF 檔案為什么可以變小?原因如下:
- 行程內已經超時的資料不再寫入檔案,
- 舊的 AOF 檔案含有無效命令,如 set a 111、set a 222 等,重寫使用行程內資料直接生成,這樣新的 AOF 檔案只保留最終資料的寫入命令,
- 多條寫命令可以合并為一個,如:lpush list a、lpush list b、lpush listc 可以轉化為:lpush list a b c,為了防止單條命令過大造成客戶端緩沖區溢位,對于 list、set、hash、zset 等型別操作,以 64 個元素為界拆分為多條,
AOF 重寫降低了檔案占用空間,除此之外,另一個目的是:更小的 AOF 檔案可以更快地被 Redis 加載,
AOF 重寫程序可以手動觸發和自動觸發:
-
手動觸發:直接呼叫
bgrewriteaof命令,該命令會將記憶體中的資料以命令的方式保存到臨時檔案中,同時會 fork 出一條新行程來將檔案重寫,
-
自動觸發:根據
auto-aof-rewrite-min-size和auto-aof-rewrite-percentage引數確定自動觸發時機,auto-aof-rewrite-min-size:表示運行 AOF 重寫時檔案最小體積,默認為 64 MB,auto-aof-rewrite-percentage:代表當前 AOF 檔案空間(aof_currentsize)和上一次重寫后 AOF 檔案空間(aof_base_size)的比值,
3.5 重啟加載
AOF 和 RDB 檔案都可以用于服務器重啟時的資料恢復,redis 重啟時加載 AOF 與 RDB 的順序是怎么樣的呢?
- 當 AOF 和 RDB 檔案同時存在時,優先加載 AOF;
- 若關閉了 AOF,加載 RDB 檔案;
- 加載 AOF/RDB 成功,redis 重啟成功;
- AOF/RDB 存在錯誤,啟動失敗列印錯誤資訊,

3.6 AOF 的優缺點
3.6.1 優點
- AOF 可以更好的保護資料不丟失,一般 AOF 會每隔 1 秒,通過一個后臺執行緒執行一次 fsync 操作,最多丟失 1 秒鐘的資料;
- AOF 日志檔案沒有任何磁盤尋址的開銷,寫入性能非常高,檔案不容易破損;
- AOF 日志檔案即使過大的時候,出現后臺重寫操作,也不會影響客戶端的讀寫;
- AOF 日志檔案的命令通過非常可讀的方式進行記錄,這個特性非常適合做災難性的誤洗掉的緊急恢復,比如某人不小心用 flushall 命令清空了所有資料,只要這個時候后臺 rewrite 還沒有發生,那么就可以立即拷貝 AOF 檔案,將最后一條 flushall 命令給刪了,然后再將該 AOF 檔案放回去,就可以通過恢復機制,自動恢復所有資料,
3.6.2 缺點
- 對于同一份資料來說,AOF 日志檔案通常比RDB資料快照檔案更大,
- AOF 開啟后,支持的寫 QPS 會比 RDB 支持的寫 QPS 低,因為 AOF 一般會配置成每秒 fsync 一次日志檔案,當然,每秒一次 fsync,性能也還是很高的,
- 以前 AO F發生過 bug,就是通過 AOF 記錄的日志,進行資料恢復的時候,沒有恢復一模一樣的資料出來,
4. 如何選擇
對于 RDB,它能夠在指定的時間間隔對記憶體中的資料進行快照存盤,
對于 AOF,他能記錄每次對服務器寫的操作,當服務器重啟的時候會重新執行這些命令來恢復原始的資料,AOF 命令以文本協議追加保存每次寫的操作到檔案末尾,Redis 還能夠對 AOF 檔案進行后臺重寫,使 AOF 檔案體積不至于過大,
兩種方式同時開啟,在兩種方式同時開啟的情況下,Redis 啟動的時候會優先加載 AOF 檔案來恢復原始資料,因為在通常情況下 AOF 檔案保存的資料集比 RDB 檔案保存的資料集要完整,RDB 的資料會不實時,
那么只使用 AOF 呢?建議不要,因為 RDB 更適合用于備份資料庫(AOF 在不斷變化,不好備份),快速重啟,而不會有 AOF 可能存在的潛在 bug,留著作為一個補救的手段,
當然,如果你只是用作快取,只希望資料在程式運行的時候存在,那么就可以不使用任何持久化方式,
| 選項 | RDB | AOF |
|---|---|---|
| 啟動優先級 | 低 | 高 |
| 體積 | 小 | 大 |
| 恢復速度 | 快 | 慢 |
| 資料安全 | 丟資料 | 三種策略 |
附:組態檔詳解
# redis行程是否以守護行程的方式運行,yes為是,no為否(不以守護行程的方式運行會占用一個終端),
daemonize no
# 指定redis行程的PID檔案存放位置
pidfile /var/run/redis.pid
# redis行程的埠號
port 6379
#是否開啟保護模式,默認開啟,要是配置里沒有指定bind和密碼,開啟該引數后,redis只會本地進行訪問,拒絕外部訪問,要是開啟了密碼和bind,可以開啟,否則最好關閉設定為no,
protected-mode yes
# 系結的主機地址
bind 127.0.0.1
# 客戶端閑置多長時間后關閉連接,默認此引數為0即關閉此功能
timeout 300
# redis日志級別,可用的級別有debug.verbose.notice.warning
loglevel verbose
# log檔案輸出位置,如果行程以守護行程的方式運行,此處又將輸出檔案設定為stdout的話,就會將日志資訊輸出到/dev/null里面去了
logfile stdout
# 設定資料庫的數量,默認為0可以使用select <dbid>命令在連接上指定資料庫id
databases 16
# 指定在多少時間內重繪次數達到多少的時候會將資料同步到資料檔案
save <seconds> <changes>
# 指定存盤至本地資料庫時是否壓縮檔案,默認為yes即啟用存盤
rdbcompression yes
# 指定本地資料庫檔案名
dbfilename dump.db
# 指定本地資料問就按存放位置
dir ./
# 指定當本機為slave服務時,設定master服務的IP地址及埠,在redis啟動的時候他會自動跟master進行資料同步
replicaof <masterip> <masterport>
# 當master設定了密碼保護時,slave服務連接master的密碼
masterauth <master-password>
# 設定redis連接密碼,如果配置了連接密碼,客戶端在連接redis是需要通過AUTH<password>命令提供密碼,默認關閉
requirepass footbared
# 設定同一時間最大客戶連接數,默認無限制,redis可以同時連接的客戶端數為redis程式可以打開的最大檔案描述符,如果設定 maxclients 0,表示不作限制,當客戶端連接數到達限制時,Redis會關閉新的連接并向客戶端回傳 max number of clients reached 錯誤資訊
maxclients 128
# 指定Redis最大記憶體限制,Redis在啟動時會把資料加載到記憶體中,達到最大記憶體后,Redis會先嘗試清除已到期或即將到期的Key,當此方法處理后,仍然到達最大記憶體設定,將無法再進行寫入操作,但仍然可以進行讀取操作,Redis新的vm機制,會把Key存放記憶體,Value會存放在swap區
maxmemory<bytes>
# 指定是否在每次更新操作后進行日志記錄,Redis在默認情況下是異步的把資料寫入磁盤,如果不開啟,可能會在斷電時導致一段時間內的資料丟失,因為redis本身同步資料檔案是按上面save條件來同步的,所以有的資料會在一段時間內只存在于記憶體中,默認為no,
appendonly no
# 指定跟新日志檔案名默認為appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的條件,有三個可選引數 - no:表示等作業系統進行資料快取同步到磁盤(快),always:表示每次更新操作后手動呼叫fsync()將資料寫到磁盤(慢,安全), everysec:表示每秒同步一次(折衷,默認值);
appendfsync everysec
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398801.html
標籤:其他
