RDB RDB是將當前數據生成快照保存到硬碟上。 RDB的工作流程: 1. 執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,如果存在bgsave命令直接返回。 2. 父進程執行fork操作創建子進程,fork操作過程中父進程被阻塞。 3. 父進程for ...
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》