Redis 是一個內存數據庫,為了保證數據不丟失,必須把數據保存到磁盤,這就叫做持久化。
Redis 有兩種持久化方法: RDB 方式以及 AOF 方式
RDB 持久化
前言
RDB持久化把內存中的數據持久化到硬盤,生成RDB文件,也就是“快照”。通過RDB文件可以重新恢復內存中的數據。RDB文件是一個壓縮過的文件,因此RDB持久化的體積比AOF小,因為存儲的是數據,所以恢復很快,性能好,但是可能會丟失最后一次持久化的數據。
RDB持久化機制的兩種方式
顯示開啟持久化
Save命令
SAVE命令會阻塞Redis服務器的主進程,直到RDB文件創建完畢。在服務器進程阻塞期間,服務器不能處理任何命令請求
BGSAVE命令
BGSAVE命令會fork一個子進程,由子進程負責創建RDB文件,父進程繼續處理命令請求
- 子進程執行BGSAVE命令期間,SAVE、BGSAVE命令會被父進程拒絕。因為Redis不允許兩個進程同時進行持久化
- BGSAVE和BGREWRITEAOF命令同樣不能同時執行,性能考慮
自動持久化機制
通過配置文件redis.conf
# 多久持久化一次🐲🐲
# 900秒內有一個key改動 300秒內10個 60秒內10000個
save 900 1
save 300 10
save 60 10000
底層實現:
-
Redis 服務器的周期性操作函數 serverCron 默認每100ms執行一次,該函數用戶對正在執行的服務器進行維護,它的其中一項工作就是檢查save 選項所設置的持久化條件是否滿足,滿足的話執行 BGSAVE 命令
-
Redis 使用 saveparams 數組保存持久化條件
-
dirty 計數器 和 lastsave 記錄上一次持久化到現在修改了幾次和上一次執行SAVE或者BGSAVE的時間戳
RDB文件的結構
參考《REDIS設計與實現》P125
后續補充,先說一下目前總結,其實也就是databases的結構:
redis 有多個數據庫,如果每個數據庫都為空,那么databases也為空。如果不是的話,database的結構差不多是這樣的:
key_value_pairs 部分保存了該數據庫中所有的鍵值對數據,如果鍵值對帶有過期時間,過期時間也會和鍵值對保存在一起。會根據value的不同類型采用不同的結構存儲
接下來看一個完整的 RDB 文件結構示例,包含0號數據庫和3號數據庫
key_value_pairs 字段詳解~(挖個坑)
分析RDB文件
使用od -c dump.rdb
命令根據rdb文件的結構人工分析,或者使用工具 redis-dump-check 分析。分析 RDB 文件其實沒有實際的意義,但可以幫助我們進一步理解 RDB 文件的結構。
AOF持久化
引言
AOF持久化與RDB持久化不同,RDB持久化把內存中的數據按照某種格式存儲到硬盤中,而AOF持久化是把所有寫命令記錄到硬盤,恢復的時候需要重新執行所有寫命令。
在這個AOF文件里面,除了用於指定數據庫的 SELECT 命令是服務器自動添加的之外,其他都是我們之前通過客戶端發送的命令。
AOF 持久化的實現機制
AOF 持久化的實現有三步:
- 命令追加:每執行完一個寫命令后,會以協議格式將被執行的寫命令追加到服務器內核的 aof_buf 緩沖區的末尾
- 命令寫入:操作系統調用 write 函數,將aof_buf 緩沖區的數據排入隊列,晚些時候一起寫入磁盤
- 命令同步:操作系統調用 fsync 和 fdatasync 兩個同步函數,強制讓操作系統立即把隊列中的數據寫入磁盤
總的來說其實就是每執行完一次寫命令(也叫做一個事件循環),redis 都會把這條命令格式化后寫到緩沖區,再從緩沖區持久化到磁盤。但是你可能會有疑問,這不是兩步嗎,為什么 write 函數不能直接把緩沖區的數據同步到磁盤,這就涉及 Linux 內核關於文件 IO 的知識了,這里簡單解釋一下。
為了提高文件的寫入效率,在現代操作系中,當用戶調用 write函數,將一些數據寫入到文件的時候,操作系統通常會將寫入數據暫時保存在一個內存緩沖區(隊列)里面, 等到隊列的空間被填滿、或者超過了指定的時限之后,才真正地將隊列中的數據寫入到磁盤里面。這種方式又叫做延遲寫。
如何開啟和配置 AOF 持久化
同樣是配置 redis.conf 文件
# 是否開啟aof持久化🐲🐲
appendonly no
# aof持久化文件名
appendfilename "appendonly.aof"
# aof持久化策略 默認是一秒持久化一次🐲🐲
# appendfsync always
appendfsync everysec
# appendfsync no
上述的持久化策略就是配置 命令同步 的時機。
- no:只有等隊列滿了數據才會持久化到磁盤。如果此時服務器宕機了,可能會丟失上次同步之后的所有命令數據。效率最高。
- always:每寫入一條命令就要同步一次,安全性最高,最多丟失一條命令的數據。但是效率最低。
- everysec:每一秒鍾執行同步一次,最多丟失一秒的數據。
如何載入AOF文件還原數據
也就是如何通過AOF文件還原數據庫的過程
- 創建一個不帶網絡連接的偽客戶端。因此redis命令只能在客戶端的上下文執行
- 從AOF 文件中分析一條寫命令
- 客戶端執行命令,循環2 3 過程
AOF重寫機制
引言
隨着服務器運行時間的流逝,可能會頻繁的對同一個 key 進行很多寫操作,AOF 文件中的內容會越來越多,文件越來越大,如果不加以控制,會對服務器造成影響,還原數據的時間也加長。
比如現在有一個鍵值對為list類型,在服務器運行時間,你對這個key進行了十次寫操作。AOF在持久化時就要保存十條命令,但是我們只保存一條命令也可以還原此key的狀態。
重寫機制就是這樣實現的:Redis 服務器創建一個新的 AOF 文件替代現有的 AOF 文件,新舊兩個文件保存的數據庫狀態相同,但是新的 AOF 文件不會包含浪費空間的冗余命令,只保存必須的命令,因此新的AOF 體積就要小得多。
觸發方式
-
手動觸發
使用
BGREWRITEAO
命令,fork 子進程重寫aof -
自動觸發,配置redis.conf
# 重寫aof文件的配置🐲🐲 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
實現機制
通過讀取服務器當前數據庫狀態來實現的,不依賴舊的 AOF 文件。
如果服務器想要用最小的命令來記錄 key 的狀態,最簡單高效的方法就是讀取 key 的值,記錄一條插入(add、push ....)的命令。一條就完事,不用在意以前的寫命令是什么。
因為AOF重寫會進行大量的寫入操作,因此 Redis 使用單個子進程來處理命令請求實現AOF重寫,縮小AOF文件體積。
為什么是進程不是線程呢?
- 子進程重寫期間,服務器父進程可以繼續處理命令請求
- 子進程有服務器進程的數據副本,不會導致線程安全問題
總結
Redis 持久化有RDB持久化和AOF持久化兩種,RDB效率高但是可能會丟失大量數據,安全性較低,AOF效率低但是安全性高。
Redis 默認使用的持久化方式是RDB持久化
AOF 重寫的目的是為了減小aof文件的體積