Redis的持久化
RDB
RDB是將當前資料生成快照儲存到硬碟上。
RDB的工作流程:
1. 執行bgsave命令,Redis父程序判斷當前是否存在正在執行的子程序,如RDB/AOF子程序,如果存在bgsave命令直接返回。
2. 父程序執行fork操作建立子程序,fork操作過程中父程序被阻塞。
3. 父程序fork完成後,bgsave命令返回“* Background saving started by pid xxx”資訊,並不再阻塞父程序,可以繼續響應其他命令。
4. 父程序建立RDB檔案,根據父程序記憶體生成臨時快照檔案,完成後對原有檔案進行原子替換。根據lastsave命令可以獲取最近一次生成RDB的時間,對應info Persistence中的rdb_last_save_time。
5. 程序傳送訊號給父程序表示完勝,父程序更新統計資訊。
對於大多數作業系統來說,fork都是個重量級操作,雖然建立的子程序不需要拷貝父程序的實體記憶體空間,但是會複製父程序的空間記憶體頁表。
子程序通過fork操作產生,佔用記憶體大小等同於父程序,理論上需要兩倍的記憶體來完成持久化操作,但Linux有寫時複製機制(copy-on-write)。父子程序會共享相同的實體記憶體頁,當父程序處理寫請求時會把要修改的頁建立副本,而子程序在fork操作過程中會共享父程序的記憶體快照。
觸發機制:
1. 手動觸發
包括save和bgsave命令。
因為save會阻塞當前Redis節點,所以,Redis內部所有涉及RDB持久化的的操作都通過bgsave方式,save方式已廢棄。
2. 自動觸發
1> 使用save的相關配置。
2> 從節點執行全量複製操作。
3> 執行debug reload命令。
4> 執行shutdown命令時,如果沒有開啟AOF持久化功能則會自動執行bgsave。
RDB的優缺點:
優點:
1. RDB是一個緊湊壓縮的二進位制檔案,代表Redis在某個時間點上的資料快照,適合備份,全量複製等場景。
2. 載入RDB恢復資料遠遠快於AOF的方式。
缺點:
沒辦法做到實時持久化/秒級持久化,因為bgsave每次執行都要執行fork操作建立子程序,屬於重量級操作,頻繁執行成本過高。
RDB的相關引數
save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./
其中,前三個引數的含義是,
#after 900 sec (15 min) if at least 1 key changed #after 300 sec (5 min) if at least 10 keys changed #after 60 sec if at least 10000 keys changed
如果要禁用RDB的自動觸發,可登出這三個引數,或者設定save ""。
stop-writes-on-bgsave-error:在開啟RDB且最近一次bgsave執行失敗的情況下,如果該引數為yes,則Redis會阻止客戶端的寫入,直到bgsave執行成功。
rdbcompression:使用LZF演算法壓縮字元物件。
rdbchecksum:從RDB V5開始,在儲存RDB檔案時,會在檔案末尾新增CRC64校驗和,這樣,能較容易的判斷檔案是否被損壞。但同時,對於帶有校驗和的RDB檔案的儲存和載入,會有10%的效能損耗。
dbfilename: RDB檔名。
dir:RDB檔案儲存的目錄。
RDB的相關變數
127.0.0.1:6379> info Persistence # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1538447605 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:155648
其含義如下:
loading: Flag indicating if the load of a dump file is on-going。是否在載入RDB檔案
rdb_changes_since_last_save: Number of changes since the last dump。
rdb_bgsave_in_progress: Flag indicating a RDB save is on-going。是否在執行bgsave操作。
rdb_last_save_time: Epoch-based timestamp of last successful RDB save。最近一次bgsave操作時的時間戳。
rdb_last_bgsave_status: Status of the last RDB save operation。最近一次bgsave是否執行成功。
rdb_last_bgsave_time_sec: Duration of the last RDB save operation in seconds。最近一次bgsave操作花費的時間。
rdb_current_bgsave_time_sec: Duration of the on-going RDB save operation if any。當前bgsave操作已經執行的時間。
rdb_last_cow_size: The size in bytes of copy-on-write allocations during the last RBD save operation。COW的大小。指的是父程序與子程序相比執行了多少修改,包括讀取緩衝區,寫入緩衝區,資料修改等。
AOF
與RDB不一樣的是,AOF記錄的是命令,而不是資料。需要注意的是,其儲存的是Redis Protocol,而不是直接的Redis命令。但是以文字格式儲存。
如何開啟AOF
只需將appendonly設定為yes就行。
AOF的工作流程:
1. 所有的寫入命令追加到aof_buf緩衝區中。
2. AOF會根據對應的策略向磁碟做同步操作。刷盤策略由appendfsync引數決定。
3. 定期對AOF檔案進行重寫。重寫策略由auto-aof-rewrite-percentage,auto-aof-rewrite-min-size兩個引數決定。
appendfsync引數有如下取值:
no: don't fsync, just let the OS flush the data when it wants. Faster. 只調用系統write操作,不對AOF檔案做fsync操作,同步硬碟操作由作業系統負責,通常同步週期最長為30s。
always: fsync after every write to the append only log. Slow, Safest. 命令寫入到aof_buf後,會呼叫系統fsync操作同步到檔案中。
everysec: fsync only one time every second. Compromise. 只調用系統write操作,fsync同步檔案操作由專門程序每秒呼叫一次。
預設值為everysec,也是建議值。
重寫機制
為什麼要重寫?重寫後可以加快節點啟動時的載入時間。
重寫後的檔案為什麼可以變小?
1. 程序內超時的資料不用再寫入到AOF檔案中。
2. 存在刪除命令。
3. 多條寫命令可以合併為一個。
重寫條件:
1. 手動觸發
直接呼叫bgrewriteaof命令。
2. 自動觸發。
與auto-aof-rewrite-percentage,auto-aof-rewrite-min-size兩個引數有關。
觸發條件,aof_current_size > auto-aof-rewrite-min-size 並且 (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage。
其中,aof_current_size是當前AOF檔案大小,aof_base_size 是上一次重寫後AOF檔案的大小,這兩部分的資訊可從info Persistence處獲取。
AOF重寫的流程。
1. 執行AOF重寫請求。
如果當前程序正在執行bgsave操作,重寫命令會等待bgsave執行完後再執行。
2. 父程序執行fork建立子程序。
3. fork操作完成後,主程序會繼續響應其它命令。所有修改命令依然會寫入到aof_buf中,並根據appendfsync策略持久化到AOF檔案中。
4. 因fork操作運用的是寫時複製技術,所以子程序只能共享fork操作時的記憶體資料,對於fork操作後,生成的資料,主程序會單獨開闢一塊aof_rewrite_buf儲存。
5. 子程序根據記憶體快照,按照命令合併規則寫入到新的AOF檔案中。每次批量寫入磁碟的資料量由aof-rewrite-incremental-fsync引數控制,預設為32M,避免單次刷盤資料過多造成硬碟阻塞。
6. 新AOF檔案寫入完成後,子程序傳送訊號給父程序,父程序更新統計資訊。
7. 父程序將aof_rewrite_buf(AOF重寫緩衝區)的資料寫入到新的AOF檔案中。
8. 使用新AOF檔案替換老檔案,完成AOF重寫。
實際上,當Redis節點執行完一個命令後,它會同時將這個寫命令傳送到AOF緩衝區和AOF重寫緩衝區。
Redis通過AOF檔案還原資料庫的流程。
1. 建立一個不帶網路連線的偽客戶端。因為Redis的命令只能在客戶端上下文中執行。
2. 從AOF檔案中分析並讀取一條命令。
3. 使用偽客戶端執行該命令。
4. 反覆執行步驟2,3,直到AOF檔案中的所有命令都被處理完。
注意:AOF的持久化也可能會造成阻塞。
AOF常用的持久化策略是everysec,在這種策略下,fsync同步檔案操作由專門執行緒每秒呼叫一次。當系統磁碟較忙時,會造成Redis主執行緒阻塞。
1. 主執行緒負責寫入AOF緩衝區。
2. AOF執行緒負責每秒執行一次同步磁碟操作,並記錄最近一次同步時間。
3. 主執行緒負責對比上次AOF同步時間。
1> 如果距上次同步成功時間在2s內,主執行緒直接返回。
2> 如果距上次同步成功時間超過2s,主執行緒會阻塞,直到同步操作完成。每出現一次阻塞,info Persistence中aof_delayed_fsync的值都會加1。
所以,使用everysec策略最多會丟失2s資料,而不是1s。
AOF的相關變數
127.0.0.1:6379> info Persistence # Persistence ... aof_enabled:1 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:0 aof_current_size:19276803 aof_base_size:19276803 aof_pending_rewrite:0 aof_buffer_length:0 aof_rewrite_buffer_length:0 aof_pending_bio_fsync:0 aof_delayed_fsync:0
其含義如下,
aof_enabled: Flag indicating AOF logging is activated. 是否開啟AOF
aof_rewrite_in_progress: Flag indicating a AOF rewrite operation is on-going. 是否在進行AOF的重寫操作。
aof_rewrite_scheduled: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete. 是否有AOF操作等待執行。
aof_last_rewrite_time_sec: Duration of the last AOF rewrite operation in seconds. 最近一次AOF重寫操作消耗的時間。
aof_current_rewrite_time_sec: Duration of the on-going AOF rewrite operation if any. 當前正在執行的AOF操作已經消耗的時間。
aof_last_bgrewrite_status: Status of the last AOF rewrite operation. 最近一次AOF重寫操作是否執行成功。
aof_last_write_status: Status of the last write operation to the AOF. 最近一次追加操作是否執行成功。
aof_last_cow_size: The size in bytes of copy-on-write allocations during the last AOF rewrite operation. 在執行AOF重寫期間,分配給COW的大小。
如果開啟了AOF,還會增加以下變數
aof_current_size: AOF current file size. AOF的當前大小。
aof_base_size: AOF file size on latest startup or rewrite. 最近一次重寫後AOF的大小。
aof_pending_rewrite: Flag indicating an AOF rewrite operation will be scheduled once the on-going RDB save is complete.是否有AOF操作在等待執行。
aof_buffer_length: Size of the AOF buffer. AOF buffer的大小
aof_rewrite_buffer_length: Size of the AOF rewrite buffer. AOF重寫buffer的大小。
aof_pending_bio_fsync: Number of fsync pending jobs in background I/O queue. 在等待執行的fsync操作的數量。
aof_delayed_fsync: Delayed fsync counter. Fsync操作延遲執行的次數。
如果一個load操作在進行,還會增加以下變數
loading_start_time: Epoch-based timestamp of the start of the load operation. Load操作開始的時間。
loading_total_bytes: Total file size. 檔案的大小。
loading_loaded_bytes: Number of bytes already loaded.已經載入的檔案的大小。
loading_loaded_perc: Same value expressed as a percentage. 已經載入的比例。
loading_eta_seconds: ETA in seconds for the load to be complete. 預計多久載入完畢。
AOF的相關引數
appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble no
其中,
no-appendfsync-on-rewrite:在執行bgsave或bgrewriteaof操作時,不呼叫fsync()操作,此時,Redis的持久化策略相當於"appendfsync none"。
aof-load-truncated:在Redis節點啟動的時候,如果發現AOF檔案已經損壞了,其處理邏輯與該引數的設定有關,若為yes,則會忽略掉錯誤,儘可能載入較多的資料,若為no,則會直接報錯退出。預設為yes。需要注意的是,該引數只適用於Redis啟動階段,如果在Redis執行過程中,發現AOF檔案corrupted,Redis會直接報錯退出。
aof-use-rdb-preamble:是否啟用Redis 4.x提供的AOF+RDB的混合持久化方案,若為yes,在重寫AOF檔案時,Redis會將資料以RDB的格式作為AOF檔案的開始部分。在重寫之後,Redis會繼續以AOF格式持久化寫入操作。預設值為no。
參考:
1. 《Redis開發與運維》
2. 《Redis設計與實現》
3. 《Redis 4.X Cookbook》