淺談:Redis持久化機制(二)AOF篇

淺談:Redis持久化機制(二)AOF篇

​ 上一篇我們提及到了redis的默認持久化方式RDB,是一種通過存儲快照數據方式持久化的機制,它在宕機後會丟失掉最後一次更新RDB文件後的數據,這也是由於它只關注於數據結果導致的。那麼我們思考一下,有沒有一種方式能夠把數據存儲、修改、刪除這種變化的過程記錄下來,也就是記錄那些set,hset,del語句,等到redis重啟後直接執行一遍這些語句即可,由此來達到數據恢復的效果呢?這樣的話是不是就不會過多的丟失數據呢?由於是記錄的過程,它可能僅僅會丟失宕機時的那一刻那一秒的數據而已。此刻,一種補充性的持久化機制AOF應運而生了,它就是一種只關注過程不關注結果的持久化機制

AOF

​ AOF(append only file)是redis持久化的另一種機制,在默認情況下是不開啟的。根據英文append only file 翻譯成英文的意思:僅僅追加文件,意思是不斷的去追加記錄那些寫入命令寫入文件中,可以明白這是一個過程的記錄。

當redis開啟AOF持久化後,redis將所有對資料庫進行過的寫入的命令(及其參數)記錄到AOF文件,以此達到記錄資料庫狀態的目的。

​ 也就是說當redis重啟之後只要按順序回放這些命令就會恢復到原始狀態了。

​ 再重申一遍,AOF會記錄過程,RDB只管結果。

AOF的持久化實現

通過配置redis.conf來進行開啟和其他的一些設置

# 可以通過修改redis。conf配置文件中的appendonly參數開啟
appendonly yes

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

#默認的文件名是appendonly.aof, 可以通過appendfilename參數修改
appendfilename appendonly.aof

AOF原理

AOF文件中存儲的是redis的命令,同步命令到 AOF 文件的整個過程可以分為三個階段,他們分別是:命令傳播,快取追加,文件寫入和保存。

  • 命令傳播:當redis的客戶端執行命令時,它會通過網路連接,將協議文本發送給redis的伺服器,伺服器會根據協議文本裡面的內容,選擇適當的命令函數,將各個參數從文本轉換為redis的字元串對象。命令執行成功後,命令參數就會被傳播到AOF程式。
  • 快取追加:AOF程式接受到那些命令參數,數據啥的,又會轉換為原來的協議文本,協議文本生成之後就會被追加到redis.h/redisServer 結構的 aof_buf 末尾。也就是把協議內容追加到了伺服器的AOF快取裡面了。
  • 文件寫入和保存:AOF 快取中的內容被寫入到 AOF 文件末尾,如果設定的 AOF 保存條件被滿足的話, fsync 函數或者fdatasync 函數會被調用,將寫入的內容真正地保存到磁碟中。(解釋:每當伺服器常規任務函數被執行、 或者事件處理器被執行時, aof.c/flushAppendOnlyFile 函數都會被調用, 這個函數執行以下兩個工作:
    • WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 文件。
    • SAVE:根據條件,調用 fsync 或 fdatasync 函數,將 AOF 文件保存到磁碟中。)

AOF保存模式

Redis目前支援三種的AOF保存模式,他們分別是:

  • AOF_FSYNC_NO:不保存
    • 調用flushAppendOnlyFile函數,WRITE都會被執行,但SAVE會被忽略。
  • AOF_FSYNC_EVERYSEC:每一秒保存一次。(默認)
    • 在這種模式中, SAVE 原則上每隔一秒鐘就會執行一次, 因為 SAVE 操作是由後檯子執行緒(fork)調用的, 所以它不會引起伺服器主進程阻塞。
  • AOF_FSYNC_ALWAYS:每執行一個命令保存一次。(不推薦,極大影響redis效率)
    • 每次執行完一個命令之後, WRITE 和 SAVE 都會被執行。
    • 因為save是由主進程執行的,所以在執行期間,主進程會被阻塞,不能接受命令請求。

AOF重寫機制

AOF不斷的記錄數據的變化過程,時間一長,數據就會越來越多,它就得需要重寫一下,進行數據的瘦身,所謂AOF重寫,無非就是把針對於某個數據的操作去除中間過程,只保留起始即可,畢竟我們想恢復的是一個最終態。舉個例子幫助理解:

命令輸入 沒有重寫的AOF數據記錄 重寫後的AOF數據記錄
step1:set singer xusong set singer xusong set singer xuezhiqian
step2:set singer wangsulong set singer wangsulong
step3:set singer xuezhiqian set singer xuezhiqian

很明顯,重寫後的AOF文件記錄的比沒有重寫的少記錄兩行,大大節省空間。

Redis不希望AOF重寫造成服務無法處理請求,所以redis決定將重寫程式放在子進程裡面執行,這樣做有幾個好處:

  • 1.子進程進行AOF重寫,不影響主程式處理其他的命令請求。
  • 2.子進程帶有主進程的數據副本,使用子進程而不是執行緒,可以避免鎖的情況,保證了數據安全性。

但是有個問題,子進程在進行AOF重寫期間,主進程還有可能繼續執行命令,導致當前資料庫的數據和重寫後的AOF文件中的數據不一致。這裡需要引入一個AOF重寫快取,關於這一塊的知識點,大家可以參考這篇大牛的部落格,寫的很詳細://blog.csdn.net/hezhiqiang1314/article/details/69396887.

另外,根據講解,我畫了一張示意圖描述整個AOF的執行過程,包含重寫:

如何觸發AOF的重寫機制呢?

  • 配置觸發,在redis.conf中配置。

    # 表示當前aof文件大小超過上一次aof文件大小的百分之多少的時候會進行重寫。如果之前沒有重寫過,以
    啟動時aof文件大小為準
    auto-aof-rewrite-percentage 100
    # 限制允許重寫最小aof文件大小,也就是文件大小小於64mb的時候,不需要進行優化
    auto-aof-rewrite-min-size 64mb
    
  • 執行bgrewriteaof命令。

如何實現混合持久化

混合持久化,顧名思義就是同時使用AOF和RDB。如果把混合持久化打開,AOF重寫的時候就直接把 RDB 的內容寫到 AOF文件開頭。

開啟混合持久化的命令:aof-use-rdb-preamble yes

AOF的文件載入以及實現數據的還原

之前也說了,AOF文件裡面保存著重建資料庫狀態所需要的所有寫命令,所以伺服器重新啟動時只需要重新的載入讀取AOF文件,執行一遍新建命令即可。

詳細步驟如下:

  • 1、創建一個不帶網路連接的偽客戶端(fake client):因為Redis的命令只能在客戶端上下文中執行,而載入AOF文件時所使用的命令直接來源於AOF文件而不是網路連接,所以伺服器使用了一個沒有網路連接的偽客戶端來執行AOF文件保存的寫命令,偽客戶端執行命令的效果和帶網路連接的客戶端執行命令的效果完全一樣
  • 2、從AOF文件中分析並讀取出一條寫命令
  • 3、使用偽客戶端執行被讀出的寫命令
  • 4、一直執行步驟2和步驟3,直到AOF文件中的所有寫命令都被處理完畢為止 當完成以上步驟之後,AOF文件所保存的資料庫狀態就會被完整地還原出來

RDB與AOF對比

  • RDB存儲的某個時刻的數據快照,採用的二進位壓縮存儲,佔用空間相對較少;AOF存儲操作命令,採用文本的存儲,佔用空間相對較多。
  • RDB由於是隔一段時間保存一次,因此性能較高;AOF因為需要保存執行過程,性能較低。
  • RDB在保存時可能後丟失最後一次快照以後更改的所有數據;AOF設置為每秒保存一次,最多也就丟失2秒的數據。
  • Redis以主伺服器模式運行,RDB不會保存過期鍵值對數據,Redis以從伺服器模式運行,RDB會保存過期鍵值對,當主伺服器向從伺服器同步時,再清空過期鍵值對;AOF寫入文件時,對過期的key會追加一條del命令,當執行AOF重寫時,會忽略過期key和del命令。