首頁 > 科技

Redis 持久化AOF你真的瞭解嗎?阿里碼農一文讓你徹底搞懂!

2021-06-26 10:44:20

大家都知道,redis相比其他cache而言,它是支援持久化的,這樣就多了一份保障,在down機之後,可以從持久化檔案中進行恢復,防止從後端資料庫重新載入,而給資料庫造成壓力。redis 支援兩種持久化的方式:一種是 AOF ,一種是RDB。今天主要來聊聊AOF持久化的方式。

寫入日誌時機

redis 在寫AOF日誌的時候,是先執行redis命令,執行redis 命令之後,才會寫入AOF日誌。這樣做的好處,是防止錯誤的命令寫入AOF日誌,同時還會省去redis 命令語法檢查的開銷,同時這樣不會阻塞redis執行緒進行寫操作。

流程如下:

日誌內容

大家看了上圖,其實就知道AOF日誌檔案存的是什麼,其實就是儲存的文字格式的命令,以set hi redis 為例,*3 表示由三部分( set hi redis )組成,$3表示 這個部分由三個位元組組成,也就是set ,其他 hi redis 都是一個道理這裡就不多做解析,如果有疑問歡迎大家留言提問。

日誌的寫到磁碟的策略

我們知道reids 的aof 日誌,最後肯定是要落盤的,寫入磁碟肯定就會受到磁碟io的影響,如果磁碟io很忙,那麼勢必會影響我們寫入磁碟的速度,而且寫入磁碟和寫入記憶體的速度肯定不是一級別的,會不會影響redis 執行緒進行返回結果呢,因為我上篇文章介紹了 redis單執行緒模型 ,也就是阻塞了redis 的主執行緒呢,這樣就增加了延遲,後續的操作就會排隊或者超時。

其實redis 為我們提供三種寫磁碟的策略,分別為Always、EverySecond、No,下面分別介紹這三種策略原理和優缺點。

Aways

所謂的always 就是執行redis 寫命令之後,立即執行寫回磁碟,寫入磁碟之後,才會返回結果,這兩部是同步進行,勢必會增加redis 的響應延遲,如果此時磁碟io很繁忙,那麼寫入磁碟就會慢,那麼redis 就會增加延遲時間。

看下圖:

1-3 步驟是順序執行,且同步執行的。

現在可以思考下,這種模式會不會丟失資料呢? 看下圖:

圖中黑色圓表示斷電了,拔出電線。

在執行第二步之前,斷電了。

答案 是會的,如果寫入記憶體成功後,然後同步執行寫回磁碟操作,這兩個步驟中,在執行寫磁碟的之前,斷電了,那麼此時這條命令的資料是沒有寫入磁碟,也就是沒有持久化成功,redis再啟動恢復的時候,aof日誌中是沒有這條記錄的。

EverySecond

所謂EverySecond 這個詞很好理解,就是每秒寫入磁碟一次。

說下具體的流程吧,其實就是redis 寫入記憶體成功後,並不是同步寫入磁碟了,而是會寫入aof日誌的緩衝區,也就是一塊記憶體,那麼寫入的速度會很快。然後redis 後臺會有一個執行緒專門讀取aof日誌緩衝區的命令,再寫入磁碟。

看圖說話:

現在思考下,這種模式,會不會丟失資料呢?

看圖說話:

圖中黑色圓表示斷電了,拔出電線。

答案是肯定的啊,因為reids 寫入記憶體之後,會把命令寫入緩衝區,緩衝區說白了 ,也是記憶體,後臺執行緒每秒讀取一次,寫入磁碟,那麼如果發生斷電呢,那麼緩衝區內的資料,也就是上一秒的資料,還沒來得及寫入磁碟就丟失了。

No

和EverySecond 類似,redis 寫入記憶體成功之後,redis 的寫命令也是會寫入到aof 緩衝區,只是此時不會由redis 後臺執行緒去執行寫入磁碟的操作了,而是有作業系統來決定,何時重新整理到磁碟。

看圖說話:

丟不丟資料,就很好看出來了,丟多少,也是由作業系統來決定了,這裡不多做說明了

三種策略的對比

至於怎麼選擇這三種策略,看自己的實際需求,資料要求的敏感度,允不允許丟失,來做一個性能和需求的取捨。

AOF日誌檔案的重寫

隨著redis 命令的增多,那麼aof日誌檔案,肯定是越來越大的,那麼大檔案在進行檔案寫入的時候,速度就會變慢,而且作業系統會對大檔案的儲存會做限制,無法儲存檔案。redis 再進行重啟資料恢復的時候,是逐一執行aof日誌的命令,如果檔案很大,那麼資料恢復的過程就會很慢,造成的影響可想而知。

那麼此時就會進行aof日誌檔案的重寫操作,那麼重寫過程中都幹了什麼呢?是在原有的aof日誌檔案內的命令進行重寫嗎?aof 重寫過程中哪些操作會阻塞redis 執行緒的讀寫操作的延遲呢?

怎麼就變大了?

變大的原因,其實也是很簡答了,命令越來越多,當然就變大了,其次對一個key 的多次操作,就會造成一個aof檔案日誌儲存了一個key 的多次操作,如果操作很頻繁的話,那麼日誌檔案增大的速度可想而知了。其實redis 在重寫的過程中,就是把一個key 的多次操作命令進行壓縮,壓縮為一條寫入命令,這樣aof 檔案就變小了,小的aof檔案無論再寫入和恢復資料的時候都會很快的。

看圖說話:

一開始對set 集合 login_user 進行了多次操作,但是在重寫之後就壓縮為了一條命令。

重寫過程

aof 的重寫,並不會是redis 主執行緒來完成的,redis會fork出一個子程序進行 aof 日誌的重寫,這個子程序交bgrewriteaof。

redis 在進行aof 重寫的過程中,並不是對原有的aof檔案進行分析重寫,而是會copy一份redis當前的記憶體頁表給子程序,這也是fork子程序的過程,如果redis的記憶體很大,那麼記憶體頁表就會很大,那麼copy的過程就會變慢,記住這個copy的過程是會阻塞redis 的主執行緒的。

copy完成之後,此時redis 主執行緒和 fork子程序就會指向相同的記憶體地址,那麼子程序就可以開始讀取記憶體地址的資料,來生成新的aof日誌了。

那麼此時可能還會有的新的請求過來,新的請求redis 必須要處理的,不能收到aof重寫的影響,此時會用到linux 的copyonwrite技術,也叫寫時複製,也就是說當修改一個已經存在的key的時候,此時會複製這個key所在的記憶體頁到新的地址,複製完成後,再進行修改key的操作,如果頁很大,這個過程就會阻塞redis 主執行緒,延遲就會增加,這是需要注意的地方。對於新進來的請求,redis 會把新命令儲存aof日誌重新緩衝區,當子程序重寫完成之後,就會讀取aof日誌重新緩衝區的內容追加到新的aof日誌中,此時aof重寫完成,舊的aof日誌就會被替換。

注意在aof重寫的過程中,新的寫請求過來,還是會往原來的aof日誌中寫入的,主要是為了防止aof重寫失敗,而不會影響舊aof日誌資料的丟失。

繼續看圖說話:

總結

今天聊了aof日誌持久化的方式,檔案內容是什麼,以及寫入磁碟的三種策略,每種策略的對redis的效能和資料可靠性都有影響,Aways、EverySecond、No 效能逐次提高,可靠性逐漸降低,具體使用要看自己對資料的實際需求。

還有就是aof 重寫,fork子程序是會阻塞redis 操作執行緒的,copyonwrite 如果遇到大頁也會增加阻塞redis 操作執行緒的時間。

aof 重寫觸發時機:

手動傳送「bgrewriteaof」指令,通過子程序生成更小體積的aof,然後替換掉舊的、大體量的aof檔案。

配置檔案配置自動觸發: auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb

在aof檔案體量超過64mb,且比上次重寫後的體量增加了100%時自動觸發重寫。

根據自己的需求來修改配置即可。

如果文章對你有幫助的話,就點贊支援一波吧,非常感謝!

如何獲取Java學習資料?

轉發分享此文,後臺私信小編:「 資料 」即可獲取。注意是私信,因為檔案比較大,所以要加好友,免費,不花錢(注:轉發分享,感謝大家)


IT145.com E-mail:sddin#qq.com