Redis設計與實現 第 11 章 AOF 持久化
第 11 章 AOF 持久化
AOF(Append Only File)持久化,通過儲存伺服器所執行的寫命令來記錄資料庫狀態
被寫入 AOF 檔案的所有命令都是以 Redis 命令請求協議儲存的,即純文字格式
11.1 AOF 持久化的實現
三個步驟:
- 命令追加
- 檔案寫入
- 檔案同步
11.1.1 命令追加
AOF 開啟時,伺服器在執行完寫命令後以協議格式將寫命令追加到伺服器狀態的 aof_buf 緩衝區末尾
11.1.2 AOF 檔案的寫入與同步
Redis 伺服器程序是一個事件迴圈,迴圈中的檔案事件負責接收客戶端的命令請求、向客戶端傳送命令回覆;時間事件負責執行類似 sercerCron 的定時執行函式
伺服器每次結束一個事件迴圈之前,會呼叫 flushAppendOnlyFile 函式,考慮是否需要將 aof_buf 緩衝區中的內容寫入和儲存到 AOF 檔案中
flushAppendOnlyFile 函式行為由配置的 appendfsync 值決定
預設為 everysec
檔案的寫入和同步
為了提高寫入效率,使用者呼叫 write 函式,將資料寫入到檔案時,作業系統通常會將寫入的資料暫時儲存在一個記憶體緩衝區中,等緩衝區填滿或者超過指定時限後,才將緩衝區資料寫入到磁碟中
但如果緩衝區資料還未寫入前計算機發生故障,緩衝區資料將會丟失
系統提供 fsync 和 fdatasync 函式,強制讓作業系統將緩衝區資料立即寫入
AOF 持久化效率和安全性
- always:效率最低,安全性最高
- everysec:足夠快,故障停機也只丟失一秒鐘的命令資料
- no:速度最快,但單次同步時間最長,平攤角度和 everysec 類似,但故障停機後會丟失自上次 AOF 後的所有命令資料
11.2 AOF 檔案的載入與資料還原
讀入檔案並重新執行一遍寫命令就可以還原伺服器關閉之前的資料庫狀態
詳細步驟:
- 建立一個不帶網路連線的偽客戶端 fake client
- 因為 Redis 命令只能在客戶端上下文執行,而還原來自 AOF 檔案而不是網路連線,所以需要一個不帶網路連線的偽客戶端,而執行效果完全一樣
- 從 AOF 檔案分析並讀取出一條寫命令
- 使用偽客戶端執行被讀出的寫命令
- 直到 AOF 檔案所有寫命令處理完畢
11.3 AOF 重寫
隨著伺服器執行,AOF 檔案內容越來越多,體積越來越大,還原的時間越多,於是就有了 AOF 重寫功能
AOF 檔案重寫功能:建立新的 AOF 檔案來代替現有的 AOF 檔案,資料庫狀態相同,但不會包含任何浪費空間的冗餘命令,於是體積會比較小
11.3.1 AOF 檔案重寫的實現
實際上 AOF 檔案重寫並不需要對現有的 AOF 檔案進入任何讀取、分析或者寫入操作,而是通過讀取伺服器當前的資料庫狀態來實現的
假設對一個鍵值對進行了非常多的操作,重寫只需要找到最終的狀態,並用一條命令去實現這個狀態即可
127.0.0.1:6379> rpush list "A" "B"
(integer) 2
127.0.0.1:6379> rpush list "C"
(integer) 3
127.0.0.1:6379> rpush list "D" "E"
(integer) 5
127.0.0.1:6379> lpop list
"A"
127.0.0.1:6379> lpop list
"B"
127.0.0.1:6379> rpush list "F" "G"
(integer) 5
127.0.0.1:6379> lrange list 0 4
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
以上命令可精簡為
127.0.0.1:6379> rpush list "C" "D" "E" "F" "G"
(integer) 5
首先從資料庫讀取鍵現在的值,用一條命令去記錄鍵值對,代替之前記錄這個鍵值對的多條命令
虛擬碼如下:
實際中,為了避免命令時造成客戶端輸入緩衝區溢位,重寫程式在處理列表、雜湊表、集合、有序集合這四種可能帶有多個元素的鍵時,會檢查元素數量,如果超過了 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD (預設為64)常量的值,那麼重寫程式將使用多條命令來記錄鍵的值
11.3.2 AOF 後臺重寫
aof_rewrite 會進行大量的寫入,呼叫這個函式的執行緒會被堵塞,如果是單執行緒工作的話將無法處理客戶端發來的命令請求
於是 AOF 重寫在子程式裡執行:
- 子程序 AOF 重寫期間,伺服器父程序可以繼續處理命令請求
- 子程序帶有伺服器程序的資料副本,使用子程序而不是執行緒,可以避免在使用鎖的情況下保證資料安全性
伺服器父程序仍在接收客戶端命令,可能有命令造成資料庫狀態修改,使得當前的資料庫狀態和重寫之後的 AOF 檔案儲存的資料庫狀態不一致
解決:
Redis 設定了一個 AOF 重寫緩衝區,在伺服器建立子程序後開始使用,當伺服器執行完寫命令後,同時將寫命令發給 AOF 緩衝區和 AOF 重寫緩衝區
在子程序執行 AOF 重寫時,伺服器程序有以下三個工作:
- 執行客戶端發來的命令
- 將執行後的寫命令追加到 AOF 緩衝區
- 將執行後的寫命令追加到 AOF 重寫緩衝區
保證:
- AOF 緩衝區內容定期寫入和同步 AOF 檔案
- 子程序建立開始,伺服器的所有寫命令會被記錄在 AOF 重寫緩衝區
子程序完成 AOF 重寫後向父程序傳送訊號,父程序接收後呼叫函式執行工作:
-
將 AOF 重寫緩衝區內容寫入新的 AOF 檔案
-
AOF 檔案改名,原子地覆蓋現有的 AOF 檔案
-
此期間阻塞父程序