共計 8484 個字符,預計需要花費 22 分鐘才能閱讀完成。
本篇內容介紹了“怎么掌握 Redis 持久化 RDB 和 AOF”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
一、為什么需要持久化?
Redis 對數據的操作都是基于內存的,當遇到了進程退出、服務器宕機等意外情況,如果沒有持久化機制,那么 Redis 中的數據將會丟失無法恢復。有了持久化機制,Redis 在下次重啟時可以利用之前持久化的文件進行數據恢復。Redis 支持的兩種持久化機制:
RDB:把當前數據生成快照保存在硬盤上。
AOF:記錄每次對數據的操作到硬盤上。
二、RDB 持久化
在指定的時間間隔內將內存中的數據集快照寫入磁盤,它恢復時是將快照文件直接讀到內存里。RDB(Redis DataBase)持久化是把當前 Redis 中全部數據生成快照保存在硬盤上。RDB 持久化可以手動觸發,也可以自動觸發。
1、備份是如何執行的?
redis 會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到臨時文件中,待持久化過程都結束了,再用這個臨時文件替換上次持久化好了的文件。整個過程中,主進程是不進行任何 IO 操作的,這就確保了極高的性能。如果需要進行大規模數據的恢復,且對數據的恢復完整性不是非常敏感, 那么 RDB 方式要比 AOF 方式更加的高效。RDB 的缺點是最后一次持久化的數據可能丟失。
2、RDB 持久化流程
3、手動觸發
save 和 bgsave 命令都可以手動觸發 RDB 持久化。
save
執行 save 命令會手動觸發 RDB 持久化,但是 save 命令會阻塞 Redis 服務,直到 RDB 持久化完成。當 Redis 服務儲存大量數據時,會造成較長時間的阻塞,不建議使用。
bgsave
執行 bgsave 命令也會手動觸發 RDB 持久化,和 save 命令不同是:Redis 服務一般不會阻塞。Redis 進程會執行 fork 操作創建子進程,RDB 持久化由子進程負責,不會阻塞 Redis 服務進程。Redis 服務的阻塞只發生在 fork 階段,一般情況時間很短。
bgsave 命令的具體流程如下圖:
1、執行 bgsave 命令,Redis 進程先判斷當前是否存在正在執行的 RDB 或 AOF 子線程,如果存在就是直接結束。
2、Redis 進程執行 fork 操作創建子線程,在 fork 操作的過程中 Redis 進程會被阻塞。
3、Redis 進程 fork 完成后,bgsave 命令就結束了,自此 Redis 進程不會被阻塞,可以響應其他命令。
4、子進程根據 Redis 進程的內存生成快照文件,并替換原有的 RDB 文件。
5、同時發送信號給主進程,通知主進程 rdb 持久化完成,主進程更新相關的統計信息(info Persitence 下的 rdb_* 相關選項)。
4、自動觸發
除了執行以上命令手動觸發以外,Redis 內部可以自動觸發 RDB 持久化。自動觸發的 RDB 持久化都是采用 bgsave 的方式,減少 Redis 進程的阻塞。那么,在什么場景下會自動觸發呢?
在配置文件中設置了 save 的相關配置,如 sava m n,它表示在 m 秒內數據被修改過 n 次時,自動觸發 bgsave 操作。
當從節點做全量復制時,主節點會自動執行 bgsave 操作,并且把生成的 RDB 文件發送給從節點。
執行 debug reload 命令時,也會自動觸發 bgsave 操作。
執行 shutdown 命令時,如果沒有開啟 AOF 持久化也會自動觸發 bgsave 操作。
5、RDB 優點
RDB 文件是一個緊湊的二進制壓縮文件,是 Redis 在某個時間點的全部數據快照。所以使用 RDB 恢復數據的速度遠遠比 AOF 的快,非常適合備份、全量復制、災難恢復等場景。
6、RDB 缺點
每次進行 bgsave 操作都要執行 fork 操作創建子經常,屬于重量級操作,頻繁執行成本過高,所以無法做到實時持久化,或者秒級持久化。
另外,由于 Redis 版本的不斷迭代,存在不同格式的 RDB 版本,有可能出現低版本的 RDB 格式無法兼容高版本 RDB 文件的問題。
7、dump.rdb 中配置 RDB
快照周期:內存快照雖然可以通過技術人員手動執行 SAVE 或 BGSAVE 命令來進行,但生產環境下多數情況都會設置其周期性執行條件。
Redis 中默認的周期新設置
# 周期性執行條件的設置格式為
save seconds changes
# 默認的設置為:save 900 1
save 300 10
save 60 10000
# 以下設置方式為關閉 RDB 快照功能
save
以上三項默認信息設置代表的意義是:
如果 900 秒內有 1 條 Key 信息發生變化,則進行快照;
如果 300 秒內有 10 條 Key 信息發生變化,則進行快照;
如果 60 秒內有 10000 條 Key 信息發生變化,則進行快照。讀者可以按照這個規則,根據自己的實際請求壓力進行設置調整。
其它相關配置
# 文件名稱
dbfilename dump.rdb
# 文件保存路徑
dir ./
# 如果持久化出錯,主進程是否停止寫入
stop-writes-on-bgsave-error yes
# 是否壓縮
rdbcompression yes
# 導入時是否檢查
rdbchecksum yes
dbfilename:RDB 文件在磁盤上的名稱。
dir:RDB 文件的存儲路徑。默認設置為“./”,也就是 Redis 服務的主目錄。
stop-writes-on-bgsave-error:上文提到的在快照進行過程中,主進程照樣可以接受客戶端的任何寫操作的特性,是指在快照操作正常的情況下。如果快照操作出現異常(例如操作系統用戶權限不夠、磁盤空間寫滿等等)時,Redis 就會禁止寫操作。這個特性的主要目的是使運維人員在第一時間就發現 Redis 的運行錯誤,并進行解決。一些特定的場景下,您可能需要對這個特性進行配置,這時就可以調整這個參數項。該參數項默認情況下值為 yes,如果要關閉這個特性,指定即使出現快照錯誤 Redis 一樣允許寫操作,則可以將該值更改為 no。
rdbcompression:該屬性將在字符串類型的數據被快照到磁盤文件時,啟用 LZF 壓縮算法。Redis 官方的建議是請保持該選項設置為 yes,因為“it’s almost always a win”。
rdbchecksum:從 RDB 快照功能的 version 5 版本開始,一個 64 位的 CRC 冗余校驗編碼會被放置在 RDB 文件的末尾,以便對整個 RDB 文件的完整性進行驗證。這個功能大概會多損失 10% 左右的性能,但獲得了更高的數據可靠性。所以如果您的 Redis 服務需要追求極致的性能,就可以將這個選項設置為 no。
8、RDB 更深入理解
由于生產環境中我們為 Redis 開辟的內存區域都比較大(例如 6GB),那么將內存中的數據同步到硬盤的過程可能就會持續比較長的時間,而實際情況是這段時間 Redis 服務一般都會收到數據寫操作請求。那么如何保證數據一致性呢?
RDB 中的核心思路是 Copy-on-Write,來保證在進行快照操作的這段時間,需要壓縮寫入磁盤上的數據在內存中不會發生變化。在正常的快照操作中,一方面 Redis 主進程會 fork 一個新的快照進程專門來做這個事情,這樣保證了 Redis 服務不會停止對客戶端包括寫請求在內的任何響應。另一方面這段時間發生的數據變化會以副本的方式存放在另一個新的內存區域,待快照操作結束后才會同步到原來的內存區域。
舉個例子:如果主線程對這些數據也都是讀操作(例如圖中的鍵值對 A),那么,主線程和 bgsave 子進程相互不影響。但是,如果主線程要修改一塊數據(例如圖中的鍵值對 C),那么,這塊數據就會被復制一份,生成該數據的副本。然后,bgsave 子進程會把這個副本數據寫入 RDB 文件,而在這個過程中,主線程仍然可以直接修改原來的數據。
在進行快照操作的這段時間,如果發生服務崩潰怎么辦?
很簡單,在沒有將數據全部寫入到磁盤前,這次快照操作都不算成功。如果出現了服務崩潰的情況,將以上一次完整的 RDB 快照文件作為恢復內存數據的參考。也就是說,在快照操作過程中不能影響上一次的備份數據。Redis 服務會在磁盤上創建一個臨時文件進行數據操作,待操作成功后才會用這個臨時文件替換掉上一次的備份。
可以每秒做一次快照嗎?
對于快照來說,所謂“連拍”就是指連續地做快照。這樣一來,快照的間隔時間變得很短,即使某一時刻發生宕機了,因為上一時刻快照剛執行,丟失的數據也不會太多。但是,這其中的快照間隔時間就很關鍵了。
如下圖所示,我們先在 T0 時刻做了一次快照,然后又在 T0+t 時刻做了一次快照,在這期間,數據塊 5 和 9 被修改了。如果在 t 這段時間內,機器宕機了,那么,只能按照 T0 時刻的快照進行恢復。此時,數據塊 5 和 9 的修改值因為沒有快照記錄,就無法恢復了。
針對 RDB 不適合實時持久化的問題,Redis 提供了 AOF 持久化方式來解決
三、AOF 持久化
AOF(Append Only File)持久化是把每次寫命令追加寫入日志中,當需要恢復數據時重新執行 AOF 文件中的命令就可以了。AOF 解決了數據持久化的實時性,也是目前主流的 Redis 持久化方式。
Redis 是“寫后”日志,Redis 先執行命令,把數據寫入內存,然后才記錄日志。日志里記錄的是 Redis 收到的每一條命令,這些命令是以文本形式保存。PS: 大多數的數據庫采用的是寫前日志(WAL),例如 MySQL,通過寫前日志和兩階段提交,實現數據和邏輯的一致性。
而 AOF 日志采用寫后日志,即先寫內存,后寫日志。
為什么采用寫后日志?
Redis 要求高性能,采用寫日志有兩方面好處:
避免額外的檢查開銷:Redis 在向 AOF 里面記錄日志的時候,并不會先去對這些命令進行語法檢查。所以,如果先記日志再執行命令的話,日志中就有可能記錄了錯誤的命令,Redis 在使用日志恢復數據時,就可能會出錯。
不會阻塞當前的寫操作
但這種方式存在潛在風險:
如果命令執行完成,寫日志之前宕機了,會丟失數據。
主線程寫磁盤壓力大,導致寫盤慢,阻塞后續操作。
1、如何實現 AOF?
AOF 日志記錄 Redis 的每個寫命令,步驟分為:命令追加(append)、文件寫入(write)和文件同步(sync)。
命令追加 當 AOF 持久化功能打開了,服務器在執行完一個寫命令之后,會以協議格式將被執行的寫命令追加到服務器的 aof_buf 緩沖區。
文件寫入和同步 關于何時將 aof_buf 緩沖區的內容寫入 AOF 文件中,Redis 提供了三種寫回策略:
Always,同步寫回:每個寫命令執行完,立馬同步地將日志寫回磁盤;
Everysec,每秒寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;
No,操作系統控制的寫回:每個寫命令執行完,只是先把日志寫到 AOF 文件的內存緩沖區,由操作系統決定何時將緩沖區內容寫回磁盤。
2、redis.conf 中配置 AOF
默認情況下,Redis 是沒有開啟 AOF 的,可以通過配置 redis.conf 文件來開啟 AOF 持久化,關于 AOF 的配置如下:
# appendonly 參數開啟 AOF 持久化
appendonly no
# AOF 持久化的文件名,默認是 appendonly.aof
appendfilename appendonly.aof
# AOF 文件的保存位置和 RDB 文件的位置相同,都是通過 dir 參數設置的
dir ./
# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no
# aof 重寫期間是否同步
no-appendfsync-on-rewrite no
# 重寫觸發配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 加載 aof 出錯如何處理
aof-load-truncated yes
# 文件重寫策略
aof-rewrite-incremental-fsync yes
以下是 Redis 中關于 AOF 的主要配置信息:
appendfsync:這個參數項是 AOF 功能最重要的設置項之一,主要用于設置“真正執行”操作命令向 AOF 文件中同步的策略。
什么叫“真正執行”呢?還記得 Linux 操作系統對磁盤設備的操作方式嗎?為了保證操作系統中 I / O 隊列的操作效率,應用程序提交的 I / O 操作請求一般是被放置在 linux Page Cache 中的,然后再由 Linux 操作系統中的策略自行決定正在寫到磁盤上的時機。而 Redis 中有一個 fsync() 函數,可以將 Page Cache 中待寫的數據真正寫入到物理設備上,而缺點是頻繁調用這個 fsync() 函數干預操作系統的既定策略,可能導致 I / O 卡頓的現象頻繁。
與上節對應,appendfsync 參數項可以設置三個值,分別是:always、everysec、no,默認的值為 everysec。
no-appendfsync-on-rewrite:always 和 everysec 的設置會使真正的 I / O 操作高頻度的出現,甚至會出現長時間的卡頓情況,這個問題出現在操作系統層面上,所有靠工作在操作系統之上的 Redis 是沒法解決的。為了盡量緩解這個情況,Redis 提供了這個設置項,保證在完成 fsync 函數調用時,不會將這段時間內發生的命令操作放入操作系統的 Page Cache(這段時間 Redis 還在接受客戶端的各種寫操作命令)。
auto-aof-rewrite-percentage:上文說到在生產環境下,技術人員不可能隨時隨地使用“BGREWRITEAOF”命令去重寫 AOF 文件。所以更多時候我們需要依靠 Redis 中對 AOF 文件的自動重寫策略。Redis 中對觸發自動重寫 AOF 文件的操作提供了兩個設置:
auto-aof-rewrite-percentage 表示如果當前 AOF 文件的大小超過了上次重寫后 AOF 文件的百分之多少后,就再次開始重寫 AOF 文件。例如該參數值的默認設置值為 100,意思就是如果 AOF 文件的大小超過上次 AOF 文件重寫后的 1 倍,就啟動重寫操作。
auto-aof-rewrite-min-size:設置項表示啟動 AOF 文件重寫操作的 AOF 文件最小大小。如果 AOF 文件大小低于這個值,則不會觸發重寫操作。注意,auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 只是用來控制 Redis 中自動對 AOF 文件進行重寫的情況,如果是技術人員手動調用“BGREWRITEAOF”命令,則不受這兩個限制條件左右。
3、深入理解 AOF 重寫
AOF 會記錄每個寫命令到 AOF 文件,隨著時間越來越長,AOF 文件會變得越來越大。如果不加以控制,會對 Redis 服務器,甚至對操作系統造成影響,而且 AOF 文件越大,數據恢復也越慢。為了解決 AOF 文件體積膨脹的問題,Redis 提供 AOF 文件重寫機制來對 AOF 文件進行“瘦身”。
圖例解釋 AOF 重寫
AOF 重寫會阻塞嗎?
AOF 重寫過程是由后臺進程 bgrewriteaof 來完成的。主線程 fork 出后臺的 bgrewriteaof 子進程,fork 會把主線程的內存拷貝一份給 bgrewriteaof 子進程,這里面就包含了數據庫的最新數據。然后,bgrewriteaof 子進程就可以在不影響主線程的情況下,逐一把拷貝的數據寫成操作,記入重寫日志。所以 aof 在重寫時,在 fork 進程時是會阻塞住主線程的。
AOF 日志何時會重寫?
有兩個配置項控制 AOF 重寫的觸發:
auto-aof-rewrite-min-size: 表示運行 AOF 重寫時文件的最小大小,默認為 64MB。
auto-aof-rewrite-percentage: 這個值的計算方式是,當前 aof 文件大小和上一次重寫后 aof 文件大小的差值,再除以上一次重寫后 aof 文件大小。也就是當前 aof 文件比上一次重寫后 aof 文件的增量大小,和上一次重寫后 aof 文件大小的比值。
重寫日志時,有新數據寫入咋整?
重寫過程總結為:“一個拷貝,兩處日志”。在 fork 出子進程時的拷貝,以及在重寫時,如果有新數據寫入,主線程就會將命令記錄到兩個 aof 日志內存緩沖區中。如果 AOF 寫回策略配置的是 always,則直接將命令寫回舊的日志文件,并且保存一份命令至 AOF 重寫緩沖區,這些操作對新的日志文件是不存在影響的。(舊的日志文件:主線程使用的日志文件,新的日志文件:bgrewriteaof 進程使用的日志文件)
而在 bgrewriteaof 子進程完成日志文件的重寫操作后,會提示主線程已經完成重寫操作,主線程會將 AOF 重寫緩沖中的命令追加到新的日志文件后面。這時候在高并發的情況下,AOF 重寫緩沖區積累可能會很大,這樣就會造成阻塞,Redis 后來通過 Linux 管道技術讓 aof 重寫期間就能同時進行回放,這樣 aof 重寫結束后只需回放少量剩余的數據即可。最后通過修改文件名的方式,保證文件切換的原子性。
在 AOF 重寫日志期間發生宕機的話,因為日志文件還沒切換,所以恢復數據時,用的還是舊的日志文件。
總結操作:
主線程 fork 出子進程重寫 aof 日志
子進程重寫日志完成后,主線程追加 aof 日志緩沖
替換日志文件
溫馨提示
這里的進程和線程的概念有點混亂。因為后臺的 bgreweiteaof 進程就只有一個線程在操作,而主線程是 Redis 的操作進程,也是單獨一個線程。這里想表達的是 Redis 主進程在 fork 出一個后臺進程之后,后臺進程的操作和主進程是沒有任何關聯的,也不會阻塞主線程
主線程 fork 出子進程是如何復制內存數據的?
fork 采用操作系統提供的寫時復制(copy on write)機制,就是為了避免一次性拷貝大量內存數據給子進程造成阻塞。fork 子進程時,子進程時會拷貝父進程的頁表,即虛實映射關系(虛擬內存和物理內存的映射索引表),而不會拷貝物理內存。這個拷貝會消耗大量 cpu 資源,并且拷貝完成前會阻塞主線程,阻塞時間取決于內存中的數據量,數據量越大,則內存頁表越大??截愅瓿珊?,父子進程使用相同的內存地址空間。
但主進程是可以有數據寫入的,這時候就會拷貝物理內存中的數據。如下圖(進程 1 看做是主進程,進程 2 看做是子進程):
在主進程有數據寫入時,而這個數據剛好在頁 c 中,操作系統會創建這個頁面的副本(頁 c 的副本),即拷貝當前頁的物理數據,將其映射到主進程中,而子進程還是使用原來的的頁 c。
在重寫日志整個過程時,主線程有哪些地方會被阻塞?
fork 子進程時,需要拷貝虛擬頁表,會對主線程阻塞。
主進程有 bigkey 寫入時,操作系統會創建頁面的副本,并拷貝原有的數據,會對主線程阻塞。
子進程重寫日志完成后,主進程追加 aof 重寫緩沖區時可能會對主線程阻塞。
為什么 AOF 重寫不復用原 AOF 日志?
父子進程寫同一個文件會產生競爭問題,影響父進程的性能。
如果 AOF 重寫過程中失敗了,相當于污染了原本的 AOF 文件,無法做恢復數據使用。
三、RDB 和 AOF 混合方式(4.0 版本 )
Redis 4.0 中提出了一個混合使用 AOF 日志和內存快照的方法。簡單來說,內存快照以一定的頻率執行,在兩次快照之間,使用 AOF 日志記錄這期間的所有命令操作。
這樣一來,快照不用很頻繁地執行,這就避免了頻繁 fork 對主線程的影響。而且,AOF 日志也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現文件過大的情況了,也可以避免重寫開銷。
如下圖所示,T1 和 T2 時刻的修改,用 AOF 日志記錄,等到第二次做全量快照時,就可以清空 AOF 日志,因為此時的修改都已經記錄到快照中了,恢復時就不再用日志了。
這個方法既能享受到 RDB 文件快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢, 實際環境中用的很多。
四、從持久化中恢復數據
數據的備份、持久化做完了,我們如何從這些持久化文件中恢復數據呢?如果一臺服務器上有既有 RDB 文件,又有 AOF 文件,該加載誰呢?
其實想要從這些文件中恢復數據,只需要重新啟動 Redis 即可。我們還是通過圖來了解這個流程:
redis 重啟時判斷是否開啟 aof,如果開啟了 aof,那么就優先加載 aof 文件;
如果 aof 存在,那么就去加載 aof 文件,加載成功的話 redis 重啟成功,如果 aof 文件加載失敗,那么會打印日志表示啟動失敗,此時可以去修復 aof 文件后重新啟動;
若 aof 文件不存在,那么 redis 就會轉而去加載 rdb 文件,如果 rdb 文件不存在,redis 直接啟動成功;
如果 rdb 文件存在就會去加載 rdb 文件恢復數據,如加載失敗則打印日志提示啟動失敗,如加載成功,那么 redis 重啟成功,且使用 rdb 文件恢復數據;
那么為什么會優先加載 AOF 呢?因為 AOF 保存的數據更完整,通過上面的分析我們知道 AOF 基本上最多損失 1s 的數據。
“怎么掌握 Redis 持久化 RDB 和 AOF”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!