深刻理解Redis的持久化

RDB算法

RDB是將當前數據生成快照保存到硬盤上。數據庫

 

RDB的工做流程:express

1. 執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,若是存在bgsave命令直接返回。promise

2. 父進程執行fork操做建立子進程,fork操做過程當中父進程被阻塞。網絡

3. 父進程fork完成後,bgsave命令返回「* Background saving started by pid xxx」信息,並再也不阻塞父進程,能夠繼續響應其餘命令。app

4. 父進程建立RDB文件,根據父進程內存生成臨時快照文件,完成後對原有文件進行原子替換。根據lastsave命令能夠獲取最近一次生成RDB的時間,對應info Persistence中的rdb_last_save_time。運維

5. 進程發送信號給父進程表示完勝,父進程更新統計信息。性能

 

對於大多數操做系統來講,fork都是個重量級操做,雖然建立的子進程不須要拷貝父進程的物理內存空間,可是會複製父進程的空間內存頁表。spa

子進程經過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》

相關文章
相關標籤/搜索