您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么掌握Redis持久化RDB和AOF”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Redis對數據的操作都是基于內存的,當遇到了進程退出、服務器宕機等意外情況,如果沒有持久化機制,那么Redis中的數據將會丟失無法恢復。有了持久化機制,Redis在下次重啟時可以利用之前持久化的文件進行數據恢復。Redis支持的兩種持久化機制:
RDB:把當前數據生成快照保存在硬盤上。
AOF:記錄每次對數據的操作到硬盤上。
在指定的時間間隔內將內存中的數據集快照寫入磁盤,它恢復時是將快照文件直接讀到內存里。RDB(Redis DataBase)持久化是把當前Redis中全部數據生成快照保存在硬盤上。RDB持久化可以手動觸發,也可以自動觸發。
redis會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到臨時文件中,待持久化過程都結束了,再用這個臨時文件替換上次持久化好了的文件。整個過程中,主進程是不進行任何IO操作的,這就確保了極高的性能。如果需要進行大規模數據的恢復,且對數據的恢復完整性不是非常敏感,那么RDB方式要比AOF方式更加的高效。RDB的缺點是最后一次持久化的數據可能丟失。
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_*相關選項)。
除了執行以上命令手動觸發以外,Redis內部可以自動觸發RDB持久化。自動觸發的RDB持久化都是采用bgsave
的方式,減少Redis進程的阻塞。那么,在什么場景下會自動觸發呢?
在配置文件中設置了save
的相關配置,如sava m n
,它表示在m秒內數據被修改過n次時,自動觸發bgsave
操作。
當從節點做全量復制時,主節點會自動執行bgsave
操作,并且把生成的RDB文件發送給從節點。
執行debug reload
命令時,也會自動觸發bgsave
操作。
執行shutdown
命令時,如果沒有開啟AOF持久化也會自動觸發bgsave
操作。
RDB文件是一個緊湊的二進制壓縮文件,是Redis在某個時間點的全部數據快照。所以使用RDB恢復數據的速度遠遠比AOF的快,非常適合備份、全量復制、災難恢復等場景。
每次進行bgsave
操作都要執行fork操作創建子經常,屬于重量級操作,頻繁執行成本過高,所以無法做到實時持久化,或者秒級持久化。
另外,由于Redis版本的不斷迭代,存在不同格式的RDB版本,有可能出現低版本的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。
由于生產環境中我們為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(Append Only File)持久化是把每次寫命令追加寫入日志中,當需要恢復數據時重新執行AOF文件中的命令就可以了。AOF解決了數據持久化的實時性,也是目前主流的Redis持久化方式。
Redis是“寫后”日志,Redis先執行命令,把數據寫入內存,然后才記錄日志。日志里記錄的是Redis收到的每一條命令,這些命令是以文本形式保存。PS: 大多數的數據庫采用的是寫前日志(WAL),例如MySQL,通過寫前日志和兩階段提交,實現數據和邏輯的一致性。
而AOF日志采用寫后日志,即先寫內存,后寫日志。
為什么采用寫后日志?
Redis要求高性能,采用寫日志有兩方面好處:
避免額外的檢查開銷:Redis 在向 AOF 里面記錄日志的時候,并不會先去對這些命令進行語法檢查。所以,如果先記日志再執行命令的話,日志中就有可能記錄了錯誤的命令,Redis 在使用日志恢復數據時,就可能會出錯。
不會阻塞當前的寫操作
但這種方式存在潛在風險:
如果命令執行完成,寫日志之前宕機了,會丟失數據。
主線程寫磁盤壓力大,導致寫盤慢,阻塞后續操作。
AOF日志記錄Redis的每個寫命令,步驟分為:命令追加(append)、文件寫入(write)和文件同步(sync)。
命令追加 當AOF持久化功能打開了,服務器在執行完一個寫命令之后,會以協議格式將被執行的寫命令追加到服務器的 aof_buf 緩沖區。
文件寫入和同步 關于何時將 aof_buf 緩沖區的內容寫入AOF文件中,Redis提供了三種寫回策略:
Always,同步寫回:每個寫命令執行完,立馬同步地將日志寫回磁盤;
Everysec,每秒寫回:每個寫命令執行完,只是先把日志寫到AOF文件的內存緩沖區,每隔一秒把緩沖區中的內容寫入磁盤;
No,操作系統控制的寫回:每個寫命令執行完,只是先把日志寫到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
”命令,則不受這兩個限制條件左右。
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文件,無法做恢復數據使用。
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”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。