Redis AOF 文件重寫流程


Redis 作為一種非常流行的內存數據庫,通過將數據保存在內存中,Redis 得以擁有極高的讀寫性能。但是一旦進程退出,Redis 的數據就會全部丟失。
為了解決這個問題,Redis 提供了 RDB 和 AOF 兩種持久化方案,將內存中的數據保存到磁盤中,避免數據丟失。本文將重點討論AOF持久化方案,以及其存在的一些問題,並探討在Redis 7.0 (已發布RC1) 中Multi Part AOF(下文簡稱為MP-AOF,本特性由阿里雲數據庫Tair團隊貢獻)設計和實現細節。

一  AOF


AOF( append only file )持久化以獨立日志文件的方式記錄每條寫命令,並在 Redis 啟動時回放 AOF 文件中的命令以達到恢復數據的目的。
由於AOF會以追加的方式記錄每一條redis的寫命令,因此隨着Redis處理的寫命令增多,AOF文件也會變得越來越大,命令回放的時間也會增多,為了解決這個問題,Redis引入了AOF rewrite機制(下文稱之為AOFRW)。AOFRW會移除AOF中冗余的寫命令,以等效的方式重寫、生成一個新的AOF文件,來達到減少AOF文件大小的目的。

二  AOFRW


圖1展示的是AOFRW的實現原理。當AOFRW被觸發執行時,Redis首先會fork一個子進程進行后台重寫操作,該操作會將執行fork那一刻Redis的數據快照全部重寫到一個名為temp-rewriteaof-bg-pid.aof的臨時AOF文件中。 
由於重寫操作為子進程后台執行,主進程在AOF重寫期間依然可以正常響應用戶命令。因此,為了讓子進程最終也能獲取重寫期間主進程產生的增量變化,主進程除了會將執行的寫命令寫入aof_buf,還會寫一份到aof_rewrite_buf中進行緩存。在子進程重寫的后期階段,主進程會將aof_rewrite_buf中累積的數據使用pipe發送給子進程,子進程會將這些數據追加到臨時AOF文件中(詳細原理可參考[1])。
當主進程承接了較大的寫入流量時,aof_rewrite_buf中可能會堆積非常多的數據,導致在重寫期間子進程無法將aof_rewrite_buf中的數據全部消費完。此時,aof_rewrite_buf剩余的數據將在重寫結束時由主進程進行處理。
當子進程完成重寫操作並退出后,主進程會在backgroundRewriteDoneHandler 中處理后續的事情。首先,將重寫期間aof_rewrite_buf中未消費完的數據追加到臨時AOF文件中。其次,當一切准備就緒時,Redis會使用rename 操作將臨時AOF文件原子的重命名為server.aof_filename,此時原來的AOF文件會被覆蓋。至此,整個AOFRW流程結束。

 

 

圖1 AOFRW實現原理

三  AOFRW存在的問題


1  內存開銷


由圖1可以看到,在AOFRW期間,主進程會將fork之后的數據變化寫進aof_rewrite_buf中,aof_rewrite_buf和aof_buf中的內容絕大部分都是重復的,因此這將帶來額外的內存冗余開銷。
在Redis INFO中的aof_rewrite_buffer_length字段可以看到當前時刻aof_rewrite_buf占用的內存大小。如下面顯示的,在高寫入流量下aof_rewrite_buffer_length幾乎和aof_buffer_length占用了同樣大的內存空間,幾乎浪費了一倍的內存。

aof_pending_rewrite:0

aof_buffer_length:35500

aof_rewrite_buffer_length:34000

aof_pending_bio_fsync:0

  

當aof_rewrite_buf占用的內存大小超過一定閾值時,我們將在Redis日志中看到如下信息。可以看到,aof_rewrite_buf占用了100MB的內存空間且主進程和子進程之間傳輸了2135MB的數據(子進程在通過pipe讀取這些數據時也會有內部讀buffer的內存開銷)。
對於內存型數據庫Redis而言,這是一筆不小的開銷。

3351:M 25 Jan 2022 09:55:39.655 * Background append only file rewriting started by pid 6817
3351:M 25 Jan 2022 09:57:51.864 * AOF rewrite child asks to stop sending diffs.
6817:C 25 Jan 2022 09:57:51.864 * Parent agreed to stop sending diffs. Finalizing AOF...
6817:C 25 Jan 2022 09:57:51.864 * Concatenating 2135.60 MB of AOF diff received from parent.
3351:M 25 Jan 2022 09:57:56.545 * Background AOF buffer size: 100 MB

 

AOFRW帶來的內存開銷有可能導致Redis內存突然達到maxmemory限制,從而影響正常命令的寫入,甚至會觸發操作系統限制被OOM Killer殺死,導致Redis不可服務。

2  CPU開銷


CPU的開銷主要有三個地方,分別解釋如下:

  1. 在AOFRW期間,主進程需要花費CPU時間向aof_rewrite_buf寫數據,並使用eventloop事件循環向子進程發送aof_rewrite_buf中的數據:
  1. 在子進程執行重寫操作的后期,會循環讀取pipe中主進程發送來的增量數據,然后追加寫入到臨時AOF文件:
  1. 在子進程完成重寫操作后,主進程會在backgroundRewriteDoneHandler 中進行收尾工作。其中一個任務就是將在重寫期間aof_rewrite_buf中沒有消費完成的數據寫入臨時AOF文件。如果aof_rewrite_buf中遺留的數據很多,這里也將消耗CPU時間。

AOFRW帶來的CPU開銷可能會造成Redis在執行命令時出現RT上的抖動,甚至造成客戶端超時的問題。

3  磁盤IO開銷


如前文所述,在AOFRW期間,主進程除了會將執行過的寫命令寫到aof_buf之外,還會寫一份到aof_rewrite_buf中。aof_buf中的數據最終會被寫入到當前使用的舊AOF文件中,產生磁盤IO。同時,aof_rewrite_buf中的數據也會被寫入重寫生成的新AOF文件中,產生磁盤IO。因此,同一份數據會產生兩次磁盤IO。

轉載自阿里雲公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM