Redis-AOF持久化
Redis持久化-AOF
一. AOF持久化原理
AOF(append only file)持久化是以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF檔案中的命令達到恢復資料的目的。AOF主要作用是解決了資料持久化的實時性,目前已經是redis持久化的主流方式。
二. 開啟AOF持久化
(1)開啟AOF持久化,需要設定appendonly引數的值為yes,預設不開啟。
(2)aof檔名通過appendfilename配置設定,預設檔名為appendonly.aof
(3)aof檔案儲存路徑通過dir引數來指定。
redis.conf中的相關配置如下:
appendonly yes(此處需修改為yes,啟用aof,預設為no)
appendfilename "appendonly.aof"
dir "/home/devuser/software/redis/redis-3.2.4/redis_instance/8001"
三 AOF的工作流程
3.1 工作流程
(1) 所有寫命令會被追加aof_buf(緩衝區)中
(2) AOF緩衝區根據對應的策略向硬碟做同步操作
(3) 隨著AOF檔案越來越大,需要定期對AOF檔案進行重寫,達到壓縮的目的
(4) 當 redis伺服器進行重啟時,可以加在AOF檔案進行資料恢復
3.2 命令寫入
(1)AOF命令寫入的內容直接是文字協議格式,因為該協議具有很好的相容性,可讀性,方便直接修改和處理。開啟AOF後,所有寫入命令都包含追加操作,直接採用協議格式避免了二次處理的開銷。
(2)命令寫入只是將命令追加到AOF緩衝區中,因為redis是採用單執行緒響應命令,如果每次寫AOF命令都直接追加到硬碟,那麼效能完全取決於當前硬碟的負載。先寫入AOF緩衝區中還有另一個好處,redis可以提供多種緩衝區同步硬碟的策略,在效能和安全性方面作曲權衡。
3.3 檔案同步
3.3.1 AOF緩衝區同步檔案策略
Redis提供了多種AOF緩衝區同步檔案策略,由引數appendfsync控制。
apendfsync引數的選項如下:
always:命令寫入AOF緩衝區後呼叫系統fsync操作同步到AOF檔案中,fsync完成後執行緒返回
everysec(預設配置):命令寫入AOF緩衝區後呼叫系統write操作,write完成後執行緒返回。fsync同步檔案操作由專門執行緒每秒呼叫一次。Everysec是建議的同步策略。理論上只有在系統突然宕機的情況下丟失1秒的資料。
no:命令寫入AOF緩衝區後呼叫系統write操作,write完成後執行緒後執行緒返回,不對AOF檔案作fsync同步,同步硬碟操作由作業系統負責,通常同步週期最長30秒。
3.3.2 系統呼叫write和fsync說明
(1)write
Wirte操作會觸發延遲寫機制。在寫入系統緩衝區後直接返回,同步硬碟操作依賴於系統排程機制。同步硬碟之前,如果系統故障宕機,緩衝區內的資料將丟失。
(2)fsync
Fsync針對單個檔案操作(比如AOF檔案),做強制硬碟同步,fsync將阻塞直到寫入硬碟完成後返回,保證了資料持久化。
3.4 檔案重寫
3.4.1 AOF檔案重寫的作用
隨著命令的不斷寫入,AOF檔案越來越大。AOF檔案重寫是把Redis程序內的資料轉化為寫命令同步到新AOF檔案的過程。更小的AOF檔案降低了檔案佔用空間,也可以更快的被redis載入。
3.4.2 AOF檔案重寫後變小的原因
1) 程序內已經超時的資料不再寫入檔案
2) 舊的AOF檔案中含有無效的命令,如seta111,重寫使用程序內資料直接生成,這樣新的AOF檔案只保留最終資料寫入命令。
3) 多條寫命令可以合併為1個,如:lpushlist a、lpush list b可以合併為lpush list a b,為防止單條命令過大造成客戶端緩衝區溢位,對於list,set,zset,hash等操作,以64個元素為界拆分為多條。
3.4.3 AOF重寫觸發方式
3.4.3.1 手動觸發
直接呼叫bgrewriteaof命令
3.4.3.2 自動觸發
根據auto-aof-rewrite-min-size和auto-aof-rewrite-percentage引數確定自動觸發時機。
auto-aof-rewrite-min-size:表示執行AOF重寫時aof檔案的最小體積。預設為64MB。
auto-aof-rewrite-percentage:代表當前AOF檔案空間(aof_current_size)和上一次重寫後AOF檔案空間(aof_base_size)的比值
自動觸發時機=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage
Reids.conf中的預設設定:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof_current_size和aof_base_size可以通過infopersistence
10.3.34.101:6378> info persistence
# Persistence
loading:0
rdb_changes_since_last_save:3
rdb_bgsave_in_progress:0
rdb_last_save_time:1500430990
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:0
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_current_size:924
aof_base_size:817
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0
3.4.4 AOF重寫工作流程
(1) 執行AOF重寫請求
a. 如果當前程序正在執行AOF重寫,當前請求不執行,並返回如下響應:
ErrorBackground append only file rewriting already in progress
b. 如果當前程序正在執行bgsave操作,重寫命令延遲到bgsave操作結束後再執行,並返回如下響應:
Background append only file rewriting scheduled
(2) 父程序執行fork操作建立子程序,fork操作執行過程中,redis父程序會阻塞。
(3) 主程序fork操作完成後,繼續響應其他命令。所有修改命令繼續寫入AOF緩衝區(AOF_BUF)並根據appendfsync策略同步到硬碟,保證原有AOF機制的正確性。
(4) 由於fork操作運用寫時複製技術,子程序只能共享fork操作時的記憶體資料。由於父程序依然響應命令,Redis使用“AOF重寫緩衝區”儲存這部分資料,防止新AOF檔案生成期間丟失這部分資料。
(5) 子程序根據記憶體快照,按照命令合併規則寫入到新的AOF檔案。每次批量寫入硬碟數量由配置aof-rewrite-incremental-fsync控制,預設為32MB,防止單次刷盤資料過多造成阻塞。
(6) 新AOF檔案寫入完成後,子程序傳送訊號給父程序,父程序更新統計資訊
(7) 父程序將AOF重寫緩衝區的資料寫入到新的AOF檔案
(8) 使用新的AOF檔案替換老檔案,完成AOF重寫。
3.5 重啟載入
AOF和RDB都可以用於伺服器重啟時的資料恢復。下圖表示Redis持久化檔案的載入流程。
流程說明:
1.AOF持久化開啟且存在AOF檔案時,優先載入AOF檔案,列印如下日誌:
*DB loaded from append only file: 5.841 seconds
2.AOF關閉或者AOF檔案不存在時,載入RDB檔案,列印如下日誌:
* DB loaded from disk:5.586seconds
3. 載入AOF/RDB檔案存在錯誤,Redis啟動失敗並列印錯誤資訊
3.6 檔案校驗
載入損壞的AOF檔案時會拒絕啟動,並列印如下日誌:
# Bad file format reading the append onlyfile:make a backup of your AOF file.then user ./redis-check-aof–fix<filename>
對於錯誤格式的AOF檔案,先進行備份,然後採用redis-check-aof–fix命令進行修復,修復後使用diff –u對比資料差異,找出丟失的資料,有些可以人工修改補全。
AOF檔案可能存在結尾不完整的情況,比如機器突然掉電導致AOF尾部檔案命令寫入不全。Redis為我們提供了aof-load-truncated配置來相容這種情況,預設開啟。載入AOF時,當遇到此問題時會忽略並繼續啟動,同時列印如下警告日誌;
#!!! Warningshort read while looading the AOF file !!!
#!!! Truncatingthe AOF at offset 397856725 !!!
#!!! AOF loadedanyway because aof-load-truncated is enabled
四, 問題定位與優化
4.1 fork操作
Fork操作會阻塞redis程序,對於高流量的redis例項ops可達5萬以上,如果fork操作耗時在秒級別將拖慢Redis幾萬條命令執行。正常情況下fork耗時應該是每GB消耗20毫秒左右。可以在info stats統計中檢視latest_fork_usec指標獲取最近一次fork操作耗時,單位微秒。
如何改善fork操作的耗時:
(1) 優先使用物理機或者高效支援fork操作的虛擬化技術,避免使用Xen。
(2) 控制Redis例項最大可用記憶體,fork耗時跟記憶體量成正比,線上建議每個Redis例項記憶體控制在10GB以內。
(3) 合理配置Linux記憶體分配策略,避免實體記憶體不足導致fork失敗。
(4) 降低fork操作的頻率,如適度放寬AOF自動觸發時機,避免不必要的全量複製等。
4.2 子程序開銷監控和優化
4.2.1 CPU
Redis屬於CPU密集型操作,通常子程序對單核CPU利用率接近90%。
(1) 不要繫結單核CPU操作。由於子程序非常消耗CPU,會和父程序產生單核資源競爭。
(2) 不要和其他CPU密集型服務部署在一起,造成CPU過渡競爭
(3) 如果部署多個REDIS例項,儘量保證同一時間只有一個子程序執行重寫工作。
4.2.2 記憶體
子程序通過fork操作產生,佔用記憶體大小等同於父程序,理論上需要兩倍記憶體來完成持久化操作,但Linux有寫時複製機制。父子程序會共享相同的實體記憶體頁,當父程序處理寫請求時會把要修改的頁建立副本,而子程序在fork操作過程中共享整個父程序記憶體快照。
(1) 如果部署多個Redis例項,儘量保證同一時刻只有一個子程序在工作。
(2) 避免大量寫入時做子程序重寫操作,這樣將導致父程序維護大量頁副本,造成記憶體消耗
(3) Linux kernel在2.6.38核心增加了TransarentHuge Pages(THP)。支援huge page(2MB)的頁分配,預設開啟。當開啟時可以降低fork建立子程序的速度,但執行fork後,如果開啟THP,複製單位從原來的4KB變為2MB,會大幅增加重寫期間父程序記憶體消耗,建議設定“sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled“,關閉THP。
4.2.3 硬碟
子程序主要職責是將AOF或者RDB檔案寫入磁碟持久化。勢必造成硬碟寫入壓力。根據Redis重寫AOF/RDB的資料量,結合系統工具如sar、iostat、iotop等,可分析出重寫期間硬碟負載情況。
硬碟開銷優化方法:
(1) 不要和其他高硬碟負載的服務部署在一起,如:儲存服務、訊息佇列服務等
(2) AOF重寫時會消耗大量硬碟IO,可以開啟配置no-appendfsync-on-rewrtie,預設關閉。表示在AOF重寫期間不做fsync操作。如果配置,有可能丟失整個AOF重寫期間的資料,需要根據資料安全性決定是否配置。
(3) 當開啟AOF功能的redis用於高流量寫入場景時,如果使用普通機械磁碟,寫入吞吐一般在100MB/S左右,這時Redis例項的瓶頸主要在AOF同步硬碟上。
(4) 對於單機配置多個Redis例項的情況,可以配置不同例項分盤儲存AOF檔案,分攤磁碟寫入壓力
4.3 AOF追加阻塞
阻塞流程分析:
(1) 主執行緒負責寫入AOF緩衝區
(2) AOF執行緒負責每秒執行一次同步磁碟操作,並記錄最近一次同步時間
(3) 主執行緒負責對比上次AOF同步時間
a. 如果距上次同步成功時間在2秒內,主執行緒直接返回。
b. 如果距上次同步成功時間超過2秒,主執行緒將會阻塞,直到同步操作完成。
通過對AOF阻塞流程可以發現兩個問題:
a. everysec配置最多可能丟失2秒資料,不是1秒
b. 如果系統fsync緩慢,將會導致Redis主執行緒阻塞影響效率
AOF阻塞問題定位:
(1) 發生AOF阻塞時,Redis輸出如下日誌,:
Asynchronous AOF fsync is taking toolong(disk is busy). Wrting the AOF buffer without waiting for fsync tocomplete,this may slow down Redis
(2) 每當發生AOF追加阻塞事件發生時,在info persistence統計中,aof_delayed_fsync指標會累加,檢視這個指標方便定位AOF阻塞問題
AOF同步最多允許2秒的延遲,當延遲發生時,說明硬碟存在高負責問題,可以通過監控工具如iotop,定位消耗硬碟IO資源的程序。