Redis基礎篇(三)持久化:AOF日志


Redis是內存數據庫,但是一旦服務器宕機,內存中的數據將會全部丟失。

最簡單的恢復方式是從后端數據庫恢復,但這種方式有兩個問題:

  1. 頻繁訪問數據庫,會給數據庫帶來巨大的壓力;
  2. 從數據庫中讀取相比從Redis中讀取要慢很多,會導致應用響應變慢

因此,Redis要實現持久化,避免從后端數據庫中進行恢復。

Redis有兩種持久化機制:AOF(Append Only File)日志和RDB快照。今天先來學習AOF日志。

什么是AOF日志?

AOF日志是通過保存Redis寫命令來記錄數據庫數據的。大多數的數據庫采用的是寫前日志(WAL),例如MySQL,通過寫前日志兩階段提交,實現數據和邏輯的一致性。想了解更多關於兩階段提交的內容,點擊查看

而AOF日志采用寫后日志,即先寫內存,后寫日志。

為什么采用寫后日志?

Redis要求高性能,采用寫日志有兩方面好處:

  1. 避免額外的檢查開銷
  2. 不會阻塞當前的寫操作

但這種方式存在潛在風險:

  1. 如果命令執行完成,寫日志之前宕機了,會丟失數據。
  2. 主線程寫磁盤壓力大,導致寫盤慢,阻塞后續操作。

如何實現AOF日志?

AOF日志記錄Redis的每個寫命令,步驟分為:命令追加(append)、文件寫入(write)和文件同步(sync)。

命令追加

當AOF持久化功能打開了,服務器在執行完一個寫命令之后,會以協議格式將被執行的寫命令追加到服務器的 aof_buf 緩沖區。

文件寫入和同步

關於何時將 aof_buf 緩沖區的內容寫入AOF文件中,Redis提供了三種寫回策略:

  • Always,同步寫回:每個寫命令執行完,立馬同步地將日志寫回磁盤;
  • Everysec,每秒寫回:每個寫命令執行完,只是先把日志寫到AOF文件的內存緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;
  • No,操作系統控制的寫回:每個寫命令執行完,只是先把日志寫到AOF文件的內存緩沖區,由操作系統決定何時將緩沖區內容寫回磁盤。

三種寫回策略的優缺點

上面的三種寫回策略體現了一個重要原則:trade-off,取舍,指在性能和可靠性保證之間做取舍

關於AOF的同步策略是涉及到操作系統的 write 函數和 fsync 函數的,在《Redis設計與實現》中是這樣說明的:

為了提高文件寫入效率,在現代操作系統中,當用戶調用write函數,將一些數據寫入文件時,操作系統通常會將數據暫存到一個內存緩沖區里,當緩沖區的空間被填滿或超過了指定時限后,才真正將緩沖區的數據寫入到磁盤里。

這樣的操作雖然提高了效率,但也為數據寫入帶來了安全問題:如果計算機停機,內存緩沖區中的數據會丟失。為此,系統提供了fsyncfdatasync同步函數,可以強制操作系統立刻將緩沖區中的數據寫入到硬盤里,從而確保寫入數據的安全性。

如何配置AOF?

默認情況下,Redis是沒有開啟AOF的,可以通過配置redis.conf文件來開啟AOF持久化,關於AOF的配置如下:

# appendonly參數開啟AOF持久化
appendonly no

# AOF持久化的文件名,默認是appendonly.aof
appendfilename "appendonly.aof"

# AOF文件的保存位置和RDB文件的位置相同,都是通過dir參數設置的
dir ./

# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no

# aof重寫期間是否同步
no-appendfsync-on-rewrite no

# 重寫觸發配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加載aof出錯如何處理
aof-load-truncated yes

# 文件重寫策略
aof-rewrite-incremental-fsync yes

AOF重寫

AOF會記錄每個寫命令到AOF文件,隨着時間越來越長,AOF文件會變得越來越大。如果不加以控制,會對Redis服務器,甚至對操作系統造成影響,而且AOF文件越大,數據恢復也越慢。

為了解決AOF文件體積膨脹的問題,Redis提供AOF文件重寫功能來對AOF文件進行“瘦身”。Redis通過創建一個新的AOF文件來替換現有的AOF,新舊兩個AOF文件保存的數據相同,但新AOF文件沒有了冗余命令。

簡單來說,AOF重寫就是把舊AOF日志文件的多條命令,在重寫后變成新日志文件的一條命令。

AOF重寫會阻塞嗎?AOF重寫是由后台線程bgrewriteaof來完成的。

AOF重寫過程

用一句話總結:一個拷貝,兩處日志。一個拷貝指一份內存拷貝,兩處日志分別是一處正在使用的AOF日志,另一處是新的AOF重寫日志。

下圖是AOF重寫過程:

AOF重寫過程

拓展

關於AOF重寫過程的潛在阻塞風險

前面提到AOF重寫不會阻塞,指的是在AOF重寫過程不會阻塞主線程,因為是通過后台bgrewriteaof線程來執行的。

但是在fork子進程的時候,fork這個瞬間一定是會阻塞主線程的。

fork采用的是操作系統提供的寫時復制(Copy On Write)機制,避免一次性拷貝造成的阻塞。但fork子進程需要拷貝進程必要的數據結構,其中有一項是拷貝內存頁表(虛擬內存和物理內存的映射索引表),這個拷貝過程會消耗大量的CPU資源,在拷貝完成之前,整個進程是會阻塞的。

拷貝內存頁完成后,子進程與父進程指向相同的內存地址空間,也就是說此時雖然產生了子進程,但是並沒有申請與父進程相同的內存大小。

那什么時候父子進程才會真正內存分離呢?在寫發生時,才真正拷貝內存的數據,這個過程中,父進程也可能會產生阻塞風險。

因為內存分配是以頁為單位進行分配的,默認4K,如果父進程此時操作的是一個bigkey,重新申請大塊內存耗時會變長,可能會產生阻塞風險。

另外,如果操作系統開啟了內存大頁機制(Huge Page,頁面大小2M),那么父進程申請內存時阻塞的概率將會大大提高,所以在Redis機器上需要關閉Huge Page機制。

為什么AOF重寫不復用原AOF日志

有兩方面原因:

  1. 父子進程寫同一個文件會產生競爭問題,影響父進程的性能。
  2. 如果AOF重寫過程中失敗了,相當於污染了原本的AOF文件,無法做恢復數據使用。

AOF重寫需要手動觸發嗎?

可以設置自動觸發,通過配置這兩個參數auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage

  • auto-aof-rewrite-min-size:表示運行AOF重寫時文件的最小大小,默認為64MB
  • auto-aof-rewrite-percentage:當前AOF文件大小和上一次重寫后AOF文件大小的差值,再除以上一次重寫后AOF文件大小

當AOF文件大小同時超出上面兩個配置項,會觸發AOF重寫。

參考資料


免責聲明!

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



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