我們知道 Redis 是一個記憶體資料庫,也就意味著如果我們的電腦異常重新啟動或者伺服器宕機的情況下,儲存在 Redis 中的資料會丟失。
Redis 雖然是個記憶體資料庫,但是 Redis 支援 RDB 和 AOF 兩種持久化機制,將資料寫往磁碟,可以有效地避免因程序退出造成的資料丟失問題,當下次重新啟動時利用之前持久化的檔案即可實現資料恢復。
RDB(Redis DataBase) 持久化是把當前程序資料生成快照儲存到硬碟的過程。什麼是快照?你可以理解成把當前時刻的資料拍成一張照片儲存下來。
RDB持久化是指在指定的時間間隔內將記憶體中的資料集快照寫入磁碟。也是預設的持久化方式,這種方式是就是將記憶體中資料以快照的方式寫入到二進位制檔案中,預設的檔名為dump.rdb
。
前面說了,RDB 是快照的方式來儲存資料的,因此對於 RDB 來說,提供了兩種觸發機制, 手動觸發和自動觸發。
手動觸發分別對應 save
和 bgsave
命令。
save
命令會阻塞當前 Redis 伺服器,直到 RDB 過程完成為止。
因此,對於記憶體比較大的範例會造成長時間阻塞,線上環境不建議使用。
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
程序傳送訊號給父程序表示完成,父程序更新統計資訊。
除了執行命令手動觸發之外,Redis 內部還存在自動觸發 RDB 的持久化機制。自動觸發是由我們的組態檔來完成的,在redis.conf
組態檔中,有如下設定:
save
save 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
設定快照檔案的存放路徑,這個設定項一定是個目錄,而不能是檔名,預設為./
。
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 檔案並獲取對應的錯誤報告。
bgsave
備份,並把 RDB 檔案拷貝到遠端機器或者檔案系統中(如 hdfs),用於災難恢復。bgsave
每次執行都要執行 fork 操作建立子程序,屬於重量級操作,頻繁執行成本過高。針對 RDB 不適合實時持久化的問題,Redis 提供了 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
AOF 的工作流程如下:
命令寫入(append)
所有的寫入命令會追加到 aof_buf(緩衝區)中。
檔案同步(sync)
AOF 緩衝區根據對應的策略向硬碟做同步操作。
檔案重寫(rewrite)
隨著 AOF 檔案越來越大,需要定期對 AOF 檔案進行重寫,達到壓縮的目的。
重新啟動載入(load)
當 Redis 伺服器重新啟動時,可以載入 AOF 檔案進行資料恢復。
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 可以提供多種緩衝區同步硬碟的策略,在效能和安全性方面做出平衡。
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 將阻塞直到寫入硬碟完成後返回,保證了資料持久化。
隨著命令不斷寫入 AOF,檔案會越來越大,為了解決這個問題,Redis 引入 AOF 重寫機制壓縮檔案體積。AOF 檔案重寫是把 Redis 程序內的資料轉化為寫命令同步到新 AOF 檔案的過程。
重寫後的 AOF 檔案為什麼可以變小?原因如下:
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)的比值。AOF 和 RDB 檔案都可以用於伺服器重新啟動時的資料恢復。redis 重新啟動時載入 AOF 與 RDB 的順序是怎麼樣的呢?
對於 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