今天主要分享繼Redis持久化方式RDB、AOF之后的一些常用的Redis問題定位于優化方式。這里主要CPU、內存、磁盤在三個維度去分析問題!
Fork操作
當Redis做RDB或AOF重寫時,一個必不可少的操作就是執行fork操作創建子進程,對于大多數操作系統來說fork是個重量級操作
雖然fork創建的子進程不需要拷貝父進程的物理內存空間,但是會復制父進程的空間內存頁表。例如對于10GB的Redis進程,需要復制大約20MB的內存頁表,因此fork 操作耗時跟進程總內存量息息相關,如果使用虛擬化技術,特別是Xen虛擬 機,fork操作會更耗時
- 在做 RDB 或 AOF 重寫時, fork 是必不可少的
- 對于大多數操作系統來說, fork 是個重量級錯誤
- fork 會復制符進程的空間內存頁表
- 如果使用虛擬化技術, 特別是 Xen 虛擬機, fork 操作會更耗時
fork 耗時問題定位:
- 高流量的 Redis 實例 ops 可達5萬以上
- 正常情況 fork 耗時應該是每 GB 消耗 20ms 左右
- 可以用 info stats 命令查看 latest_fork_usec 指標, 獲取最近一次 fork 操作耗時, 單位微秒
如何改善 fork 操作的耗時:
- 優先使用物理機或者高效支持 fork 操作的虛擬化技術, 避免使用 Xen
- 控制 Redis 實例最大可用內存, fork 耗時跟內存量成正比, 線上建議每個 Redis 實例內存控制在 10GB 以內
- 合理配置 Linux 內存分配策略, 避免物理內存不足導致 fork 失敗, 具體細節見12.1節 “Linux 配置優化”
- 降低 fork 操作的頻率, 如適度放寬 AOF 自動觸發時機, 避免不必要的全量復制等
子進程開銷進程與優化
子進程負責AOF或者RDB文件的重寫,它的運行過程主要涉及CPU、內存、硬盤三部分的消耗
CPU
- CPU 開銷分析。子進程負責把進程內的數據分批寫入文件, 這個過程屬于 CPU 密集操作, 通常子進程對單核 CPU 利用率接近90%
- CPU 消耗優化。Redis 是 CPU 密集型服務, 不要做綁定單核 CPU 操作。由于子進程非常消耗 CPU, 會和父進程產生單核資源競爭.
- 不要和其他 CPU 密集型服務部署在一起, 造成 CPU 過度競爭
- 如果部署多個 Redis 實例, 盡量保證同一時刻只有一個子進程執行重寫工作, 具體細節見5.4節多實例部署
硬盤
- 硬盤開銷分析
子進程主要職責是把 AOF 或者 RDB 文件寫入硬盤持久化。勢必造成硬盤寫入壓力。根據 Redis 重寫 AOF/RDB 的數據量, 結合系統工具如 sar、iostat、iotop 等, 可分析出重寫期間硬盤負載情況
- 硬盤開銷優化
- 不要和其他高硬盤負載的服務部署在一起。如: 存儲服務、消息隊列服務等
- AOF 重寫時會消耗大量硬盤 IO, 可以開啟配置 no-appendfsync-on-rewrite, 默認關閉。表示在 AOF 重寫期間不做 fsync 操作
- 當開啟 AOF 功能的 Redis 用于高流量寫入場景時, 如果使用普通機械磁盤, 寫入吞吐一般在 100MB/s 左右, 這時 Redis 實例的瓶頸主要在 AOF 同步硬盤上
- 對于單機配置多個 Redis 實例的情況, 可以配置不同實例分盤存儲 AOF 文件, 分攤硬盤寫入壓力
配置 no-appendfsync-on-rewrite=yes 時, 在極端情況下可能丟失整個 AOF 重寫期間的數據,需要根據數據安全性決定是否配置
內存
- 內存消耗分析
子進程通過 fork 操作產生, 占用內存大小等同于父進程, 理論上需要兩倍的內存來完成持久化操作, 但 Linux 有寫時復制機制 (copy-on-write)。父子進程會共享相同的物理內存頁, 當父進程處理寫請求時會把要修改的頁創建副本, 而子進程在 fork 操作過程中共享整個父進程內存快照。
- 內存消耗監控
- RDB 重寫: 被修改的內存頁可以等價認為 RDB 重寫的消耗
- AOF 重寫: 被修改的內存頁 + AOF 重寫緩沖區
- 內存消耗優化
- 如果部署多個 Redis 實例, 盡量保證同一時刻只有一個子進程在工作
- 避免在大量寫入時做子進程重寫操作, 這樣將導致父進程維護大量頁副本, 造成內存消耗
Transparent Huge Pages(THP) 是 Linux kernel 在2.6.38增加的功能, 支持 huge page (2MB) 頁分配, 會降低 fork 速度, 默認開啟. 當開啟時, 在 fork 后會大幅增加重寫期間父進程的內存消耗, 建議關閉:
sudo?echo?never>/sys/kernel/mm/transparent_hugepage/enabled
AOF追加阻塞
當開啟AOF持久化時,常用的同步硬盤的策略是everysec,用于平衡性 能和數據安全性。對于這種方式,Redis使用另一條線程每秒執行fsync同步 硬盤。當系統硬盤資源繁忙時,會造成Redis主線程阻塞!如下圖所示

阻塞流程分析:
- 如果距上次同步成功時間在2秒內,主線程直接返回
- 如果距上次同步成功時間超過2秒,主線程將會阻塞,直到同步操作完 成。
- 主線程負責寫入AOF緩沖區
- AOF線程負責每秒執行一次同步磁盤操作,并記錄最近一次同步時 間
- 主線程負責對比上次AOF同步時間:
通過對AOF阻塞流程可以發現兩個問題:
- everysec配置最多可能丟失2秒數據,不是1秒
- 如果系統fsync緩慢,將會導致Redis主線程阻塞影響效率
AOF阻塞問題定位:
- 每當發生AOF追加阻塞事件發生時,在info Persistence統計中, aof_delayed_fsync指標會累加,查看這個指標方便定位AOF阻塞問題。
- AOF同步最多允許2秒的延遲,當延遲發生時說明硬盤存在高負載問 題,可以通過監控工具如iotop,定位消耗硬盤IO資源的進程
- 發生 AOF 阻塞時, Redis 輸出如下日志, 用于記錄 AOF fsync 阻塞導致拖慢 Redis 服務的行為
Asynchronous?AOF?fsync?is?taking?too?long?(disk?is?busy).?Writing?the?AOF?bufferwithout?waiting?for?fsync?to?complete,?this?may?slow?down?Redis
- 每當發生 AOF 追加阻塞事件發生時, 在info Persistence 統計中, aof_delayed_fsync 指標會累加, 查看這個指標方便定位 AOF 阻塞問題
- AOF 同步最多允許2秒的延遲, 當延遲發生時說明硬盤存在高負載問題, 可以通過監控工具如 iotop, 定位消耗硬盤 IO 資源的進程
更多原創技術分享,關注公眾號:碼農架構