注意:Linux系統使用redis需要更改系統的內存分配策略,執行sysctl vm.overcommit_memory=1命令,0-2分別代表不加載、加載、允許超額加載
一、RDB
RDB持久化是把當前數據生成二進制快照保存到硬盤的過程。
配置項:
- dir:保存路徑
- dbfilename:保存文件名
- rdbcompression:是否開啓壓縮,默認開啓,可以方便保存到硬盤和發送給從節點
觸發時機:
- save:阻塞當前Redis服務器,直到RDB過程完成,線上不建議使用
- bgsave:Redis主進程執行fork操作創建子進程,RDB持久化過程由子進程完成,阻塞只發生在fork階段。
- 配置save頻率自動觸發:save m n表示m秒內發生n次修改後自動觸發bgsave
- 從節點執行全量複製,主節點自動執行bgsave生成RDB文件併發送給從節點
- 執行debug reload命令重新加載Redis時執行save
- 如果沒有開啓aof,執行shutdown時回執行bgsave
觸發流程:
- 執行bgsave命令,如果正在執行bgsave命令則直接返回
- 父進程執行fork操作創建子進程,fork操作時會阻塞主進程
- 父進程fork完後主進程不再阻塞,可執行其它命令
- 子進程創建RDB文件,寫入完成後替換舊文件
- 子進程發送信號通知父進程表示完成,父進程更新統計信息
相關統計信息:
- 上次fork子進程耗費的微秒數:info stats 命令,latest_fork_usec 項
- 上次生成RDB的時間戳:info persistence 命令,rdb_last_save_time 項
RDB文件:
Redis加載損壞的RDB拒絕啓動會打印日誌Short read or OOM loading DB,使用 redis-check-dump 命令檢測 RDB 文件,並獲取錯誤報告。
二、AOF
記錄每次的寫命令,寫入格式爲Redis文本協議,重啓時再重新執行AOF中的命令來達到恢復數據的目的,所以恢復速度相對 RDB 較慢。
配置項:
- dir:保存路徑
- appendonly:是否開啓
- appendfilename:存儲文件名
- appendfsync:同步策略,always 每次寫入調用 fsync,everysec 每秒調用 fsync,no 由系統配置決定調用 fsync 時機,一般30s
工作流程:
- 執行 AOF 請求,如果正在執行 AOF 重寫或 bgsave 直接返回
- 父進程執行 fork 創建子進程,開銷等同 bgsave,使用寫時複製技術,子進程共享 fork 操作時的內存數據,接下來父進程的操作保存到重寫緩衝區(aof_rewrite_buf)中
- 子進程批量寫入新 AOF 文件中,每次批量寫入大小由 aof-rewrite-incremental-fsync 配置控制,開啓後每次寫入32M,防止單次刷盤數據過大造成磁盤阻塞
- 新 AOF 文件重寫完成後,發送信號給父進程,父進程更新統計信息
- 父進程將 aof_rewrite_buf 中的數據寫入到新的 AOF 文件中
- 使用新的 AOF 文件替換老文件,重寫完成
由於AOF追加阻塞造成丟失數據:
當策略爲 everysec 時,丟失數據會有以下兩種情況:
- 如果距離上次同步成功時間(aof_last_rewrite_time_sec)超過2秒,Redis主線程將會阻塞
- 如果距離上次同步成功時間在2秒內,會直接返回
如果發生上面任一種情況時宕機,會導致最多2秒的數據丟失。
發生阻塞時,會打印如下日誌:
Asynchronous AOF fsync is taking too lang (disk is busy?).
同時會增加 aof_delayed_fsync 統計項次數(通過 info persistence 查看)
重寫機制:
目的:降低AOF文件存儲空間大小,加快重新加載速度
重寫策略:
- 超時的數據不再寫入
- 重複的無效的命令以最新的爲準(比如:set why why,set why why1以後者爲準)
- 多條寫命令合併爲一個(比如:lpush list a、lpush list b、lpush list b,可以合併爲lpush list a b c)
配置項:
- auto-aof-rewrite-percentage:觸發當前AOF文件大小和上一次重寫後AOF文件大小的最小比值
- auto-aof-rewrite-min-size:運行AOF重寫時文件最小體積
相關統計信息:
- aof_current_size:當前aof文件大小,通過info persistence命令查看
- aof_base_siz:上次重寫後AOF文件大小,通過info persistence命令查看
觸發機制:
- 手動觸發:調用bgrewriteaof
- 自動觸發:aof_current_size > auto-aof-rewrite-min-size 並且 (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage 時自動觸發
三、混合持久化方式
由於 AOF 持久化方式在重啓加載數據時效率遠遠不如 RDB 方式,所以 Redis 在4.0版本後引入了混合持久化方式,配置項爲 aof-use-rdb-preamble,yes開啓,策略爲在生成或寫入 AOF 文件時,將 RDB 數據寫在前面,AOF 數據追加到後面,在每次啓動時先加載 RDB,再加載 AOF ,下面爲配置中的說明:
# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
# [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
aof-use-rdb-preamble yes
四、多實例部署
Redis 單線程架構導致無法充分利用多核 CPU 特性,所以可以在一臺機器上部署多個 Redis 實例。但是當多個實例同時開啓 AOF 重寫後,彼此之間會產生對 CPU 和 IO 的競爭,所以可以通過外部程序輪詢,依此控制每個實例的 AOF 重寫操作執行。
我們依賴的相關監控指標如下:
- rdb_bgsave_in_progress:bgsave 子進程是否正在運行
- rdb_last_save_time:當前運行 bgsave 的時間,-1表示未運行
- aof_enabled:是否開啓 AOF
- aof_rewrite_in_progress:AOF 重寫子進程是否正在運行
- aof_rewrite_scheduled:在 bgsave 結束後是否運行 AOF 重寫
- aof_current_rewrite_time_sec:當前運行 AOF 重寫的時間,-1表示未運行
- aof_current_size:AOF 文件當前字節數
- aof_base_size:AOF 上次重寫 rewrite 的字節數
代碼實現:https://github.com/why444216978/php-redis-aof
運行前看一下 aof 文件更新時間:
why@localhost] /usr/local/var/db/redis$ll
total 48
-rw-r--r-- 1 why staff 9295 5 4 15:31 appendonly.aof
-rw-r--r-- 1 why staff 9295 5 4 15:26 dump.rdb
運行後再看一下 aof 文件更新時間:
[why@localhost] /usr/local/var/db/redis$ll
total 48
-rw-r--r-- 1 why staff 9295 5 4 17:19 appendonly.aof
-rw-r--r-- 1 why staff 9295 5 4 15:26 dump.rdb
成功!