redis AOF 性能瓶頸分析

最近發現一個問題,redis 在高流量寫入的情況下,偶發性出現客戶端延遲升高,經過排查發現 redis AOF 重寫 fork 子進程導致。爲什麼要進行 AOF 重寫,以及如何避免 AOF 重寫呢?本文做個介紹。

1. 什麼是 AOF

AOF 是 redis 防止數據丟失的日誌備份策略,總共有三種方式

說到 AOF,其實很多人都會拿它跟 Rdb 去做比較,Rdb 是以二進制的方式存儲到磁盤上。體積更小,當出現服務重啓或者宕機,相對於 AOF 恢復速度更快。

另外一點,RDB 和 AOF 對客戶端的寫入性能影響,一般情況下,AOF 的寫入性能是比不上 RDB 的,因爲 AOF 多了一個寫入操作,但是隨着寫入數據量越來越大,這個差距會越來越小。看完本文後,你應該能夠找到答案。具體可以參考 redis 官網https://redis.io/topics/persistence,這裏不過多贅述。

2. AOF 重寫又是怎麼回事

很多同學對 redis 的寫 AOF 文件和 AOF 重寫傻傻分不清,無論是發生時機或者操作對象其實是沒有任何關係的。

2.1. 寫 AOF 文件

寫 AOF 文件發生在客戶端請求 redis server,這個時候就會產生一條 AOF 記錄,這條記錄何時寫入磁盤跟自身設置的 AOF 策略控制相關,可以同步、也可以異步寫入。

2.2. AOF 重寫操作

如果 redis server 接受的寫請求越來越多,那麼 AOF 文件會越來越大,爲了防止 AOF 文件無限膨脹(打爆磁盤)以及不利於 redis server 宕機後的恢復,所以要進行重寫。其實說白了就是一個重複命令合併的過程。

對於上圖幾個關鍵點:

2.3. AOF 重寫發生條件。

如下是源碼所示:

//如果AOF功能啓用、沒有RDB子進程和AOF重寫子進程在執行、AOF文件大小比例設定了閾值,以及AOF文件大小絕對值超出了閾值,進一步判斷AOF文件大小比例是否超出閾值
if (server.aof_state == AOF_ON && 
server.rdb_child_pid == -1 &&
server.aof_child_pid == -1 && 
server.aof_rewrite_perc 
&& server.aof_current_size > server.aof_rewrite_min_size) 
{.....}

看到這裏,再想想,爲什麼 redis 之所以添加各種條件限制 AOF 的發生?

儘可能減少 CPU 和 IO 消耗

3. 如何避免 AOF 造成的影響

3.1. 影響原因

上文中也說了,AOF 主要耗時發生在 fork 一個子進程並且會阻塞主進程,這是爲什麼呢?

因爲 fork 子進程時,子進程是會拷貝父進程的頁表,即虛實映射關係,但是 fork 不會把所有的內存數據都 copy 到子進程,只會 copy 一部分有用的數據到子進程中。

所以 fork 在複製內存頁的時候會大量的消耗 CPU 資源,如果複製的內存頁越大,fork 阻塞的時間就會越久。拷貝內存頁完成,子進程與父進程指向相同的內存地址,這個時候就會放開主進程的阻塞, 對外提供操作。每當有新的寫命令,就會觸發操作系統的 COW 寫時複製機制,此時就會把這新的命令寫到 AOF 日誌緩衝區,等待數據重寫完成後,重寫的日誌與緩衝區修改的數據進行合併,這樣保證了父子進程之間的數據同步。

3.1. fork 導致阻塞時長

正常情況 fork 耗時應該是每 GB 消耗 20ms 左右,當然也可以用 info stats 命令查看 latest_fork_usec 指標, 獲取最近一次 fork 操作耗時, 單位微秒。

3.2. 如何避免

總的來說,沒有好不好,只有是否合適。軟件系統的設計都是偏向於解決某個領域的問題,具體情況要看具體使用場景,比如可以考慮關閉 AOF,當服務流量低峯時手動觸發 AOF。也可以從自身的業務出發儘可能減少寫請求。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/b2_1cyasD1bTm5G9PsCIpQ