推薦學習:
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
命令的具體流程如下圖:bgsave
命令,Redis程序先判斷當前是否存在正在執行的RDB或AOF子執行緒,如果存在就是直接結束。bgsave
命令就結束了,自此Redis程序不會被阻塞,可以響應其他命令。除了執行以上命令手動觸發以外,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
命令來進行,但生產環境下多數情況都會設定其週期性執行條件。
# 週期性執行條件的設定格式為 save <seconds> <changes> # 預設的設定為: save 900 1 save 300 10 save 60 10000 # 以下設定方式為關閉RDB快照功能 save ""
以上三項預設資訊設定代表的意義是:
# 檔名稱 dbfilename dump.rdb # 檔案儲存路徑 dir ./ # 如果持久化出錯,主程序是否停止寫入 stop-writes-on-bgsave-error yes # 是否壓縮 rdbcompression yes # 匯入時是否檢查 rdbchecksum yes
bgsave
子程序相互不影響。但是,如果主執行緒要修改一塊資料(例如圖中的鍵值對 C),那麼,這塊資料就會被複制一份,生成該資料的副本。然後,bgsave
子程序會把這個副本資料寫入 RDB 檔案,而在這個過程中,主執行緒仍然可以直接修改原來的資料。針對RDB不適合實時持久化的問題,Redis提供了AOF持久化方式來解決
AOF(Append Only File)持久化是把每次寫命令追加寫入紀錄檔中,當需要恢復資料時重新執行AOF檔案中的命令就可以了。AOF解決了資料持久化的實時性,也是目前主流的Redis持久化方式。
Redis是「寫後」紀錄檔,Redis先執行命令,把資料寫入記憶體,然後才記錄紀錄檔。紀錄檔裡記錄的是Redis收到的每一條命令,這些命令是以文字形式儲存。PS: 大多數的資料庫採用的是寫前紀錄檔(WAL),例如MySQL,通過寫前紀錄檔和兩階段提交,實現資料和邏輯的一致性。
而AOF紀錄檔採用寫後紀錄檔,即先寫記憶體,後寫紀錄檔。
為什麼採用寫後紀錄檔?
Redis要求高效能,採用寫紀錄檔有兩方面好處:
但這種方式存在潛在風險:
AOF紀錄檔記錄Redis的每個寫命令,步驟分為:命令追加(append)、檔案寫入(write)和檔案同步(sync)。
預設情況下,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重寫紀錄檔期間發生宕機的話,因為紀錄檔檔案還沒切換,所以恢復資料時,用的還是舊的紀錄檔檔案。
總結操作:
溫馨提示
這裡的程序和執行緒的概念有點混亂。因為後臺的bgreweiteaof程序就只有一個執行緒在操作,而主執行緒是Redis的操作程序,也是單獨一個執行緒。這裡想表達的是Redis主程序在fork出一個後臺程序之後,後臺程序的操作和主程序是沒有任何關聯的,也不會阻塞主執行緒
主執行緒fork出子程序是如何複製記憶體資料的?
fork採用作業系統提供的寫時複製(copy on write)機制,就是為了避免一次性拷貝大量記憶體資料給子程序造成阻塞。fork子程序時,子程序時會拷貝父程序的頁表,即虛實對映關係(虛擬記憶體和實體記憶體的對映索引表),而不會拷貝實體記憶體。這個拷貝會消耗大量cpu資源,並且拷貝完成前會阻塞主執行緒,阻塞時間取決於記憶體中的資料量,資料量越大,則記憶體頁表越大。拷貝完成後,父子程序使用相同的記憶體地址空間。
但主程序是可以有資料寫入的,這時候就會拷貝實體記憶體中的資料。如下圖(程序1看做是主程序,程序2看做是子程序):
在主程序有資料寫入時,而這個資料剛好在頁c中,作業系統會建立這個頁面的副本(頁c的副本),即拷貝當前頁的物理資料,將其對映到主程序中,而子程序還是使用原來的的頁c。
在重寫紀錄檔整個過程時,主執行緒有哪些地方會被阻塞?
為什麼AOF重寫不復用原AOF紀錄檔?
Redis 4.0 中提出了一個混合使用 AOF 紀錄檔和記憶體快照的方法。簡單來說,記憶體快照以一定的頻率執行,在兩次快照之間,使用 AOF 紀錄檔記錄這期間的所有命令操作。
這樣一來,快照不用很頻繁地執行,這就避免了頻繁 fork 對主執行緒的影響。而且,AOF 紀錄檔也只用記錄兩次快照間的操作,也就是說,不需要記錄所有操作了,因此,就不會出現檔案過大的情況了,也可以避免重寫開銷。
如下圖所示,T1 和 T2 時刻的修改,用 AOF 紀錄檔記錄,等到第二次做全量快照時,就可以清空 AOF 紀錄檔,因為此時的修改都已經記錄到快照中了,恢復時就不再用紀錄檔了。
這個方法既能享受到 RDB 檔案快速恢復的好處,又能享受到 AOF 只記錄操作命令的簡單優勢, 實際環境中用的很多。
資料的備份、持久化做完了,我們如何從這些持久化檔案中恢復資料呢?如果一臺伺服器上有既有RDB檔案,又有AOF檔案,該載入誰呢?
其實想要從這些檔案中恢復資料,只需要重新啟動Redis即可。我們還是通過圖來了解這個流程:
那麼為什麼會優先載入AOF呢?因為AOF儲存的資料更完整,通過上面的分析我們知道AOF基本上最多損失1s的資料。
推薦學習:
以上就是完全掌握Redis持久化:RDB和AOF的詳細內容,更多請關注TW511.COM其它相關文章!