Redis-AOF持久化
RDB
和 AOF
區別在於: 前者儲存資料庫快照,持久化所有鍵值對,後者通過儲存 寫命令 保證資料庫的狀態.
什麼是 AOF ?
AOF
持久化通過儲存伺服器執行的寫命令實現,進行恢復時通過重放 AOF
檔案中的寫命令,來保證資料安全.就像 mysql
的 binlog
一樣.
開啟 AOF
通過在 redis.conf
中將 appendonly
設為 yes
即可
# redis.conf
appendonly yes
# 設定 aof 檔名字
appendfilename "appendonly.aof"
# Redis支援三種不同的刷寫模式:
# appendfsync always #每次收到寫命令就立即強制寫入磁碟,是最有保證的完全的持久化,但速度也是最慢的,一般不推薦使用。
appendfsync everysec #每秒鐘強制寫入磁碟一次,在效能和持久化方面做了很好的折中,是受推薦的方式。
# appendfsync no #完全依賴OS的寫入,一般為30秒左右一次,效能最好但是持久化最沒有保證,不被推薦。
複製程式碼
AOF 檔案格式
AOF
檔案格式以 redis
命令請求協議為標準的,*.aof
檔案可以直接開啟.
AOF 持久化過程
命令追加 append
redis
執行完客戶端的寫命令後,會將該命令以協議的格式寫入到 aof_buf
中.該屬性為 redisServer
中的一個.
#src/server.h
struct redisServer {
....
sds aof_buf; /* AOF buffer,written before entering the event loop */
}
複製程式碼
AOF 寫入同步
redis
的服務程式是一個 事件迴圈 - event loop
,每次迴圈大概會做三件事.
- 檔案事件: 接收客戶端的命令,返回結果
- 時間事件: 執行系統的定時任務(
serverCron
),完成漸進rehash
擴容之類的操作 - aof flush: 是否將
aof_buf
中的內容寫入檔案中
# 虛擬碼
def eventloop():
while true:
processFileEvents() # 處理命令
processTimeEvents() # 處理定時任務
flushAppendOnlyFile() # 處理 aof 寫入
複製程式碼
flushAppendOnlyFile
中的動作是否執行是根據一個配置決定的.
appendfsync
該配置有幾個值可選,預設是 everysec
.
- always: 總是寫入.只要程式執行到這一步了,就將
aof_buf
中命令協議寫入到檔案 - everysec: 每秒寫入. 每次執行前會先判斷是否與上次寫入間隔一秒,再次同步時通過 一個執行緒 專門執行
- no: 不寫入. 命令寫入
aof_buf
後由作業系統決定何時同步到檔案
fsync: 現代作業系統為了提高檔案讀寫的效率,通常會將
write
函式寫入的資料快取在記憶體中,等到快取空間填滿或者超過一定時限,再將其寫入磁碟.這樣的問題在於宕機時快取中的資料就無法恢復.所以作業系統提供了 fsync/fdatasync 兩個函式,強製作業系統將資料立即寫入磁碟,保證資料安全.兩函式區別在於: 前者會更新檔案的屬性,後者只更新資料.
三種模式在效能和資料上都有相對的優缺點. always
模式資料安全性更強,畢竟每次都是直接寫入,但是就會影響效能.磁碟讀寫是比較慢的. everysec
模式效能較好,但會丟失一秒內的快取資料. no
模式就完全取決於作業系統了.
AOF 還原資料
AOF 重寫
AOF
重寫的意思其實就是對單個命令的多個操作進行整理,留下最終態的執行命令來減少 aof
檔案的大小.你可以想象一下執行 1w 次 incr
操作,寫入 aof
1w 次的場景.
觸發條件
AOF
重寫可以自動觸發.通過配置 auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
,滿足條件就會自動重寫.具體可以檢視官方的 redis.conf
重寫過程
- 建立子程式,根據記憶體裡的資料重寫
aof
,儲存到temp
檔案 - 此時主程式還會接收命令,會將寫操作追加到舊的
aof
檔案中,並儲存在server.aof_rewrite_buf_blocks
中,通過管道傳送給子程式存在server.aof_child_diff
中,最後追加到temp
檔案結尾 - 子程式重寫完成後退出,主程式根據子程式退出狀態,判斷成功與否。成功就將剩餘的
server.aof_rewrite_buf_blocks
追加到temp file
中,然後rename()
覆蓋原aof
檔案
重寫的過程中主程式還是會一直接受客戶端的命令,所以重寫子程式與主程式肯定會存在資料不一致的情況.redis
針對這種情況作出瞭解決方案: 新增一個 aof_rewrite_buf_blocks
,aof
寫入命令時,不僅寫入到 aof_buf
,如果正在重寫,那麼也寫入到 aof_rewrite_buf_blocks
中,這樣在子程式重寫完畢後,可以將 aof_rewrite_buf_blocks
的命令追加到新檔案中,保證資料不丟失.
rename
操作是原子的,也是唯一會造成主程式阻塞的操作.