開發與維運

Redis 分享-AOF的阻塞簡單記錄

背景

團隊內每週延安大學分享中,redis中討論了一個AOF會阻塞主線程的問題,這個問題之前沒考慮到過,一直以為fork了子進程一定不會阻塞,沒有深度考慮。

因為大家也沒有深入研究,這裡整理一下,因為這塊確實實踐較少,可能有描述不準確的,大家多多提意見指正,我多多完善。

AOF

redis中文官網:http://www.redis.cn/topics/persistence.html

什麼是AOF

以下定義均來自中文官網

只追加操作的文件(Append-only file,AOF)

快照功能(RDB)並不是非常耐久(dura ble): 如果 Redis 因為某些原因而造成故障停機, 那麼服務器將丟失最近寫入、且仍未保存到快照中的那些數據。 從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化。

你可以在配置文件中打開AOF方式:

appendonly yes

從現在開始, 每當 Redis 執行一個改變數據集的命令時(比如 SET), 這個命令就會被追加到 AOF 文件的末尾。這樣的話, 當 Redis 重新啟時, 程序就可以通過重新執行 AOF 文件中的命令來達到重建數據集的目的。

日誌重寫

因為 AOF 的運作方式是不斷地將命令追加到文件的末尾, 所以隨著寫入命令的不斷增加, AOF 文件的體積也會變得越來越大。舉個例子, 如果你對一個計數器調用了 100 次 INCR , 那麼僅僅是為了保存這個計數器的當前值, AOF 文件就需要使用 100 條記錄(entry)。然而在實際上, 只使用一條 SET 命令已經足以保存計數器的當前值了, 其餘 99 條記錄實際上都是多餘的。

為了處理這種情況, Redis 支持一種有趣的特性: 可以在不打斷服務客戶端的情況下, 對 AOF 文件進行重建(rebuild)。執行 BGREWRITEAOF 命令, Redis 將生成一個新的 AOF 文件, 這個文件包含重建當前數據集所需的最少命令。

Redis 2.2 需要自己手動執行 BGREWRITEAOF 命令; Redis 2.4 則可以自動觸發 AOF 重寫, 具體信息請查看 2.4 的示例配置文件。

通過官網基本可以瞭解了什麼是AOF和重寫,如果可以想了解AOF和RDB的區別,官網也有描述可以通過上面鏈接去閱讀。

rdb:默認開啟,文件小,格式緊湊,數據恢復快,比較適合用來做災備,服務宕機丟數據更多一些

aof:默認關閉,文件大,數據恢復慢,但是數據更加完整,支持多種同步策略。

官方推薦使用混合模式。

AOF寫入策略

由appendfsync參數控制:

可配置的值 說明
always 命令寫入buf後調用系統調用fsync同步AOF文件,fsync完成後線程返回。
no 命令寫入buf後調用系統調用write操作,後續fsync同步操作由操作系統來完成,一般為30秒一次。
everysec 命令寫入buf後調用系統調用write操作,後續fsync同步操作專門線程每一秒調用一次。

everysec是always和no的折中,是性能和安全性的這種,是redis默認的配置,也是比較推薦的配置。

重寫流程圖

重寫流程:
  1. bgrewriteaof 觸發重寫,判斷是否當前有bgsave或bgrewriteaof在運行,如果有,則等待該命令結束後再繼續執行
  2. 主進程fork出子進程執行重寫操作,保證主進程不會阻塞
  3. 子進程遍歷redis內存中數據到臨時文件,客戶端的寫請求同時寫入aof_buf緩衝區和aof_rewrite_buf重寫緩衝區 保證原AOF文件完整以及新AOF文件生成期間的新的數據修改動作不會丟失
  4. 1).子進程寫完新的AOF文件後,向主進程發信號,父進程更新統計信息。2).主進程把aof_rewrite_buf中的數據寫入到新的AOF文件
  5. 使用新的AOF文件覆蓋舊的AOF文件,完成AOF重寫


流程圖來源:https://blog.csdn.net/wsdc0521/article/details/106765809

阻塞

雖然在everysec配置下aof的fsync是由子線程進行操作的,但是主線程會監控fsync的執行進度。

主線程在執行時候如果發現上一次的fsync操作還沒有返回(也有一種對比上一次的fsync操作時間,大於2秒阻塞的),那麼主線程就會阻塞。

解決阻塞

  1. 修改配置為yes,減少IO競爭:

no-appendfsync-on-rewrite yes/no

    • 設置為yes,重寫期間會停止appendfsync操作,避免io競爭((表示在日誌重寫時,不進行命令追加操作,而只是將命令放在重寫緩衝區裡,避免與命令的追加造成磁盤IO上的衝突))。但是這樣的風險:如果在aof重寫期間redis宕機了,那麼aof的數據便會丟失,可靠性下降
    • 配置就是設置為no時候,aof重寫期間還是會執行fsync,這個時候就會產生IO競爭,有可能阻塞主線程。如果當前AOF文件很大,那麼相應的rewrite時間會變長,appendfsync被阻塞的時間也會更長
  1. 為了保證可靠性,進行集群化
    • 給當前Redis實例添加slave節點,當前節點設置為master, 然後master節點關閉AOF,slave節點開啟AOF。
    • 這樣的方式的風險是如果master掛掉,尚沒有同步到salve的數據會丟失。而且 集群選舉時,aof配置怎麼根據選舉結果動態修改修改,可能需要手動上去改一遍。感覺運維成本比較大。
  1. 閒的時間定時重寫、提升服務器io性能
    1. 在凌晨低峰期定時手動執行bgrewriteaof命令完成每日一次的AOF重寫
    2. 比如使用ssd
      1. 將配置設為no
      2. 硬盤採用高速固態硬盤SSD
    1. 如果是三方服務器,購買私有或者用戶隔離的混合雲,不要購買共有云,不要讓其他用戶影響該服務器磁盤穩定性
  1. 上監控人工介入,監控所有系統必不可少的一部分。
    1. 在重寫時為了避免硬盤空間不足或者IO使用率高影響重寫功能,我們還添加了硬盤空間報警和IO使用率報警保

擴展鏈接

Leave a Reply

Your email address will not be published. Required fields are marked *