Redis 持久化之 RDB 和 AOF
RDB持久化是把當前程序資料生成快照儲存到硬碟的過程,觸發RDB持久化過程分為手動觸發和自動觸發。
觸發機制
手動觸發分別對應save 和bgsave
- save 阻塞redis 伺服器,直到RDB過程完成,對於記憶體比較大的例項會造成長時間阻塞,不建議線上環境使用。
- bgsave,fork 子程序完成RDB,阻塞只發生在fork 階段,一般時間很短。
自動觸發
-
save m n m表示秒內資料集存在n次修改時,自動觸發bgsave。
-
從節點執行全量複製操作,主節點自動執行bgsave生成RDB檔案併發送從節點。
- 執行 debug reload 命令會重新載入redis,會自動觸發save操作。
- 預設執行shutdown,如果沒有開啟AOF 會自動執行bgsave。
流程
-
執行bgsave 命令,父程序判斷當前是否存在正在執行的子程序,如RDB/AOF 子進行,如果存在bgsave 直接返回結果。
-
父程序執行fork,fork 過程中會阻塞父程序,通過info stats 檢視latest_fork_use 選項,可以獲取最近一次fork 的耗時,單位是微秒。
127.0.0.1:6379> INFO stats # Stats …… latest_fork_usec:612 ……
-
父程序fork 完,bgsave 命令會返回
background saveing started
資訊,並不在阻塞父程序,可以繼續響應其他命令。127.0.0.1:6379> BGSAVE Background saving started
-
子程序建立RDB 檔案,根據父程序記憶體生成臨時快照檔案,完成後對原有檔案進行原子替換。執行lastsave 命令可以獲取最後一次生成RDB 的時間,對應 info 統計中的rdb_last_save_time 選項。
127.0.0.1:6379> LASTSAVE (integer) 1556158402
- 程序傳送訊號給父程序表示完成,父程序更新統計資訊,具體見 info Persistence下的 rdb_* 相關選項。
127.0.0.1:6379> INFO stats # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1556158402 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:24240128 aof_enabled:0 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
配置
RDB 優缺點
RDB的優點:
- RDB是一個緊湊壓縮的二進位制檔案,代表Redis在某個時間點上的資料快照。非常適用於備份,全量複製等場景。比如每6小時執行bgsave備份,並把RDB檔案拷貝到遠端機器或者檔案系統中(如hdfs),用於災難恢復。
- Redis載入RDB恢復資料遠遠快於AOF的方式。
RDB的缺點:
- RDB方式資料沒辦法做到實時持久化/秒級持久化。因為bgsave每次執行都要執行fork操作建立子程序,屬於重量級操作,頻繁執行成本過高。
- RDB檔案使用特定二進位制格式儲存,Redis版本演進過程中有多個格式的RDB版本,存在老版本Redis服務無法相容新版RDB格式的問題。針對RDB不適合實時持久化的問題,Redis提供了AOF持久化方式來解決。
AOF
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF檔案中的命令達到恢復資料的目的。AOF的主要作用是解決了資料持久化的實時性,目前已經是Redis持久化的主流方式。理解掌握好AOF持久化機制對我們兼顧資料安全性和效能非常有幫助。
使用
配置
cat redis.conf | grep ^append appendonly no appendfilename "appendonly.aof" appendfsync everysec
AOF的工作流程操作:命令寫入(append)、檔案同步(sync)、檔案重寫(rewrite)、重啟載入(load)
流程
- 所有寫入追加到 aof_buf 中。
- AOF 緩衝區根據對應的策略向硬碟做同步操作。
- 隨著AOF檔案的變大,需要定期對AOF 檔案進行重寫,達到壓縮目的。
- 當redis 伺服器重啟時,可以載入AOF檔案進行資料恢復。
為什麼採用文字方式寫入
AOF 寫入採用文字方式,理由如下:
- 相容性好
- 開啟AOF 後,所有寫操作都是追加的,避免了二次處理開銷。
- 文字協議具有可讀性,方便直接修改和處理。
為什麼要把命令追加到AOF_BUF
redis 使用單執行緒響應命令,如果每次寫AOF 都直接追加到磁碟,效能會很差,先寫入快取區,然後在同步到磁碟效能會有很大提升。
AOF 的檔案同步
可配置值 | 說明 |
---|---|
always | 命令寫人aof_ buf 後呼叫系統fsync操作同步到AOF檔案,fsync 完成後執行緒返回 |
everysec | 命令寫入aof_ buf後呼叫系統write操作,write完成後執行緒返回。fsync 同步檔案操作由專門執行緒每秒呼叫- -次 |
no | 命令寫人aof_ buf 後呼叫系統write操作,不對AOF檔案做fsync同步,同步硬碟操作由作業系統負責,通常同步週期最長30秒 |
系統呼叫write和fsync說明:
- write操作會觸發延遲寫(delayed write)機制。Linux在核心提供頁緩衝區用來提高硬碟IO效能。write操作在寫入系統緩衝區後直接返回。同步硬碟操作依賴於系統排程機制,例如:緩衝區頁空間寫滿或達到特定時間週期。同步檔案之前,如果此時系統故障宕機,緩衝區內資料將丟失。
- fsync針對單個檔案操作(比如AOF檔案),做強制硬碟同步,fsync將阻塞直到寫入硬碟完成後返回,保證了資料持久化。
AOF 重寫
隨著不斷寫入AOF 檔案不斷變大,redis 通過AOF 重寫機制壓縮檔案體積。把程序內的資料轉化為寫命令同步到新的AOF檔案。
重寫AOF的優勢
- 降低檔案佔用,提升redis 載入速度。
AOF 分手動和自動觸發
- 手動: bgrewriteaof
- 自動,根據配置檔案中的下面2個引數確定自動觸發時間
# cat redis.conf |grep ^auto-aof auto-aof-rewrite-percentage 100# 表示當前AOF 檔案空間(aof_current_size)和上一次重寫後AOF檔案空間(aof_base_size)的比 auto-aof-rewrite-min-size 64mb # 表示執行AOF重寫時的檔案最小體積
自動觸發時機=aof_current_size>auto-aof-rewrite-minsize&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewritepercentage其中aof_current_size和aof_base_size可以在info Persistence統計資訊中查。
流程說明
-
執行AOF 重寫請求,如果當前有程序正在執行AOF重寫,請求不執行並返回
ERR Background append only file rewriting already in progre
如果當前正在執行bgsave,重寫命令延遲到bgsave 完成後執行,返回
Background append only file rewriting schedul
-
父程序fork 子程序,開銷等同於bgsave。
AOF重寫緩衝區
-
子程序根據記憶體快照,按命令合併規則寫入到新的AOF 檔案。每次批量寫入磁碟資料由配置
置aof-rewrite-incremental-fsync
控制,預設為32MB,防止單次刷盤資料過多造成硬碟阻塞。 - 新AOF 檔案寫入後,子程序傳送訊號給父程序,更新統計資訊。
- 父程序把AOF 重寫快取區的資料寫入到新AOF檔案。
- 使用新AOF檔案替換老檔案,完成AOF重寫。
問題定位與優化
fork 操作耗時問題排查
redis 做 RDB 或 AOF 重寫時,會進行fork,雖然fork建立的子程序不需要拷貝父程序的實體記憶體空間,但是會複製父程序的空間記憶體頁表。例如對於10GB的Redis程序,需要複製大約20MB的記憶體頁表,因此fork操作耗時跟程序總記憶體量息息相關,如果使用虛擬化技術,特別是Xen虛擬機器,fork操作會更耗時。
如何改善fork操作的耗時:
1)優先使用物理機或者高效支援fork操作的虛擬化技術,避免使用Xen。
2)控制Redis例項最大可用記憶體,fork耗時跟記憶體量成正比,線上建議每個Redis例項記憶體控制在10GB以內。
3)合理配置Linux記憶體分配策略,避免實體記憶體不足導致fork失敗。
4)降低fork操作的頻率,如適度放寬AOF自動觸發時機,避免不必要的全量複製等。
子程序開銷監控和優化
CPU
-
CPU開銷分析。子程序負責把程序內的資料分批寫入檔案,這個過程屬於CPU密集操作,通常子程序對單核CPU利用率接近90%.
-
CPU消耗優化。Redis是CPU密集型服務,不要做繫結單核CPU操作。由於子程序非常消耗CPU,會和父程序產生單核資源競爭。
- 不要和其他CPU密集型服務部署在一起,造成CPU過度競爭。
- 如果部署多個Redis例項,儘量保證同一時刻只有一個子程序執行重寫工作。
記憶體
-
記憶體消耗分析。子程序通過fork操作產生,佔用記憶體大小等同於父進
程,理論上需要兩倍的記憶體來完成持久化操作,但Linux有寫時複製機制(copy-on-write)。父子程序會共享相同的實體記憶體頁,當父程序處理寫請求時會把要修改的頁建立副本,而子程序在fork操作過程中共享整個父程序記憶體快照。
-
記憶體消耗監控。