在《Redis:持久化之RDB》的最後,我們說到RDB持久化存在一個問題那就是一旦Redis發送異常退出,就會丟失最後一次快照以後更改的所有數據,這可能是幾秒或者更長的一個時間丟失。我們也說到如果無法忍受近幾秒或者更長的數據丟失,則可以考慮使用AOF方式進行持久化。那麼AOF方式又是怎樣的呢?往下看。
1. AOF簡介
RDB是通過保存數據庫中鍵值對的數據來達到持久化的目的,而AOF則是通過保存Redis服務器所執行的寫命令來記錄數據庫狀態。比如以下命令:
RDB持久化方式則是將aof-test-key1
和aof-test-key2
兩個鍵值對保存在RDB的快照文件中,而對於AOF持久化方式則是將兩條set
命令保存在AOF文件中。
2. 開啓AOF
默認情況下Redis不開啓AOF方式的持久化,但我們可以通過appendonly
參數啓用:
appendonly yes
開啓AOF持久化後每執行一條修改數據的命令,Redis就會將該命令寫入磁盤的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通過dir
參數進行設置。默認情況下,AOF持久化生成的文件名爲appendonly.aof
,我們也可以通過appendfilename
參數修改:
appendfilename appendonly.aof
3. 數據恢復
接下來演示一下,Redis基於AOF方式恢復數據的過程。
場景一:驗證AOF持久化已經在起作用了。
(1)我們先刪除dump.rdb
和appendonly.aof
文件,然後寫入k1,然後通過kill
命令殺死進程。
(2)可以看到重新生成了appendonly.aof文件,此時我們重啓redis服務,看k1的值是否被持久化。(可以看到k1的值被持久化,而該值就是從AOF文件恢復回來的。)
場景二:驗證redis啓動時,是從AOF文件恢復持久化數據,而非dump.rdb。
(1)我們在設置一個k2的值,然後通過redis-cli shutdown
停掉redis進程。
(2)重啓redis服務,此時dump.rdb和appendonly.aof都記錄了k2的值,無法看出redis重啓是否是從aof中恢復的。那麼我們再寫入k3,然後通過kill -9殺死redis進程。(我們知道用kill方式殺死進程,持久化數據是不會被寫進dump.rdb中的)
(3)重啓redis服務,看下k3是否被持久化,若被持久化,則可驗證redis啓動時,從aof文件中恢復數據。
4. AOF重寫
先看下面一組命令:
正常來說,Redis的AOF持久化文件會記錄這三條命令,然而我們知道k1鍵值對最終的結果是3,也就是說前兩條命令其實是冗餘的,因爲這兩條的執行結果會被第三條命令覆蓋。隨着執行的命令越來越多,AOF文件的大小也會越來越大,即使內存中實際的數據可能並沒有多少。AOF文件越大的另外一個問題就是在Redis進行數據修復的時間也會越長。
很自然地,Redis提供了AOF文件的優化機制,就上面的一組命令而言,Redis在達到一定條件時會自動將前兩條無用的記錄刪除,只保留第三條,這樣就能大大減小AOF文件的大小。
4.1 重寫的條件
和重寫相關的條件配置如下:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage
:當目前AOF文件大小超過上一次重寫時AOF文件大小的百分之多少時再次進行重寫,如果之前沒有重寫過,則以啓動時的AOF文件大小爲依據。auto-aof-rewrite-min-size
:設置允許重寫的最小AOF文件大小,通常情況下當AOF文件很小的時候,即使有很多冗餘的命令我們也不需要關心。
當然除了讓Redis自動執行重寫外,我們也可以通過bgrewirteaof
命令手動執行AOF重寫。
4.2 重寫的原理
我們知道Redis是單線程的,如果重寫AOF需要比較長的時間,那麼在重寫AOF的時候,Redis將長時間無法處理其它的命令,這顯然是不合乎情理的。所以Redis在重寫的時候,是將重寫程序放到子進程進行,這樣:
(1)子進程進行 AOF 重寫期間,服務器進程(父進程)可以繼續處理其他命令。
(2)子進程帶有父進程的數據副本,使用子進程而不是線程,可以在避免使用鎖的情況下,保證數據的安全性。
使用子進程解決了上面的問題,但是新問題也產生了:因爲子進程在進行 AOF 重寫期間,服務器進程依然在處理其它命令,這新的命令有可能也對數據庫進行了修改操作,使得當前數據庫狀態和重寫後的 AOF 文件狀態不一致。
爲了解決這個數據狀態不一致的問題,Redis 服務器設置了一個 AOF 重寫緩衝區,這個緩衝區是在創建子進程後開始使用,當Redis服務器執行一個寫命令之後,就會將這個寫命令也發送到 AOF 重寫緩衝區。當子進程完成 AOF 重寫之後,就會給父進程發送一個信號,父進程接收此信號後,就會調用函數將 AOF 重寫緩衝區的內容都寫到新的 AOF 文件中。
5. 同步磁盤數據
雖然打開AOF持久化方式後,Redis會將寫命令記錄下來,但實際上,由於操作系統的緩存機制,數據並沒有真正地立即寫入磁盤,而是進入系統的磁盤緩存。在默認情況下,系統每30秒會執行一次同步操作,以便將磁盤緩存中的內容真正落盤,假設在這30秒的過程中系統異常退出則會導致磁盤緩存中的數據丟失。
一般來講啓動AOF持久化的應用都無法忍受這樣的損失,那麼就需要Redis在寫入AOF文件後主動要求系統將磁盤緩存中的內容同步到磁盤中。在Redis中,我們可以通過appendfsync
參數設置同步的時機:
# appendfsync always
appendfsync everysec
# appendfsync no
默認情況下Redis採用everysec
規則,即每秒執行一次同步操作。
always
:表示每次執行寫入都會執行同步,這是最安全也是最慢的方式。no
:表示不主動進行同步操作,由操作系統每隔30秒做一次同步,這是最快但也是最不安全的方式。
Redis允許同時開啓AOF和RDB持久化,此時重新啓動Redis後Redis會使用AOF文件來恢復數據,因爲相對而言,AOF方式的持久化可能丟失的數據更少。