淺談: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重寫快取,關於這一塊的知識點,大家可以參考這篇大牛的部落格,寫的很詳細:https://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命令。