談談redis的持久化機制

說道redis的持久化機制,相信有經驗的開發人員可能都知道,rdb,aof。但是不知道大家有沒有仔細的去想過幾個問題:
(1)rdb和aof到底是如何工作的?
(2)既然有了rdb爲什麼還要有aof呢?
(3)如果我既設置了rdb又設置了aof,那麼到底聽誰的?
(4)aof的混合持久化又是怎麼回事呢?

下面我主要圍繞這幾個方面,結合一些具體的案例。來跟大家分享一下我這邊的總結心得。

首先我們來看rdb

當然首先得知道rdb是什麼東西,如何去設置:

RDB:原理是redis會單獨創建(fork)一個與當前進程一模一樣的子進程來進行持久化,這個子進程的所有數據(變量。環境變量,程序程序計數器等)都和原進程一模一樣,會先將數據寫入到一個臨時文件中,待持久化結束了,再用這個臨時文件替換上次持久化好的文件,整個過程中,主進程不進行任何的io操作,這就確保了極高的性能

rdb系統所有相關配置項如下,這邊都做了中文詳細解釋。如何配置?:
關於redis所有配置詳細解釋,可以參考我另一篇博文:
https://blog.csdn.net/baomw/article/details/103789050

# 指定在多長時間內,有多少次更新操作,就將數據同步到數據文件,可以多個條件配合
# 這裏表示900秒(15分鐘)內有1個更改,300秒(5分鐘)內有10個更改以及60秒內有10000個更改
# 如果想禁用RDB持久化的策略,只要不設置任何save指令,或者給save傳入一個空字符串參數也可以
#由於rdb持久化是需要fork子進程去進行備份操作,非常消耗資源,一般生產使用不會設置太高的頻率
#一般只留第一個 save 900 1配置即可
save 900 1
save 300 10
save 60  1

# 默認情況下,如果 redis 最後一次的後臺保存失敗,redis 將停止接受寫操作,這樣以一種強硬的方式讓用戶知道數據不能正確的持久化到磁盤, 
# 否則就會沒人注意到災難的發生。 如果後臺保存進程重新啓動工作了,redis 也將自動的允許寫操作。
# 如果配置成no,表示你不在乎數據不一致或者有其他的手段發現和控制
stop-writes-on-bgsave-error yes

# 對於存儲到磁盤中的快照(rdb),可以設置是否進行壓縮存儲。如果是的話,redis會採用
# LZF算法進行壓縮。如果你不想消耗CPU來進行壓縮的話,可以設置爲關閉此功能
rdbcompression yes

# 在存儲快照後,還可以讓redis使用CRC64算法來進行數據校驗,但是這樣做會增加大約
# 10%的性能消耗,如果希望獲取到最大的性能提升,可以關閉此功能
rdbchecksum yes

#rdb文件的名字。
dbfilename dump.rdb

# dbfilename文件存放目錄。必須是一個目錄,aof文件也會保存到該目錄下。
dir ./

衆所周知,redis是單線程的,下面我畫了一個單機版的redis執行流程:
在這裏插入圖片描述
據上我們來分下下,redis執行rdb持久化的時候,爲什麼要fork一個子線程:
因爲redis是單線程的,如果這個線程(主線程)去進行rdb持久化了,那麼此時有客戶端向我們服務端發送命令,他是得不到處理的(唯一的線程在做持久化),必須要等持久化結束後才能接受客戶端命令。那麼這個過程可以理解爲客戶端發送命令會阻塞(上圖中的阻塞隊列),這樣所有的客戶端請求都得不到及時處理。如果fork一個子線程,就可以保證rdb的異步執行,不影響客戶端並行執行。

redis中執行rdb的幾種場景如下:
(1)滿足配置文件的條件(如15分鐘內有一個key改變)
(2)執行shutdown時,如果沒有開啓aof,會觸發一次rdb
(3)執行命令save或者bgsave命令時,但是save或者bgsave有點區別

	 bgsave:會fork子進程,原理與rdb上面原理一樣,系統默認觸發rdb持久化都是調用的此命令
	 save:是不會fork子進程的,它使用主進程進行持久化,所以會導致客戶端命令發送到我們服務端得不到及時處理,所以他是阻塞的

(4)執行flushall命令時

下面來看看aof:

原理是將Reids的操作日誌以追加的方式寫入文件,但是隻記錄增改操作,讀操作是不記錄的,
整體分爲三步:數據寫入內存–》數據寫入aof_buf—》寫入持久化文件。
第二步到第三步什麼時候執行根據配置文件觸發機制(注意:aof持久化不會fork子進程)

apf相關redis配置如下:

	############################## APPEND ONLY MODE ###############################
	# 是否啓用aof持久化方式 。否在每次更新操作後進行日誌記錄,Redis在默認情況下是異步的把數據寫入磁盤,如果不開啓,可能會在斷電時導致一	 			    
	# 段時間內的數據丟失。
 	# 因爲 redis本身同步數據文件是按上面save條件來同步的,所以有的數據會在一段時間內只存在於內存中。默認爲no
	appendonly no

	# The name of the append only file (default: "appendonly.aof")
	# 指定更新日誌(aof)文件名,默認爲appendonly.aof
	appendfilename "appendonly.aof"
	
	#指定更新日誌條件,共有3個可選值: 
	#  no:表示等操作系統進行數據緩存同步到磁盤(快,持久化沒保證) 
	#  always:同步持久化,每次發生數據變更時,立即記錄到磁盤(慢,安全) 
	#  everysec:表示每秒同步一次(默認值,很快,但可能會丟失一秒以內的數據,一般生產環境使用此配置即可)
	# appendfsync always
	appendfsync everysec
	# appendfsync no
	
	# 指定是否在後臺aof文件rewrite期間調用fsync,默認爲no,表示要調用fsync(無論後臺是否有子進程在刷盤)。
	# Redis在後臺寫RDB文件或重寫AOF文件期間會存在大量磁盤IO,此時,在某些linux系統中,調用fsync可能會阻塞。
	#如果應用系統無法忍受延遲,而可以容忍少量的數據丟失,則設置爲yes。如果應用系統無法忍受數據丟失,則設置爲no。
	no-appendfsync-on-rewrite no

	#當AOF文件增長到一定大小的時候Redis能夠調用 BGREWRITEAOF 對日誌文件進行重寫 。
	#當AOF文件大小的增長率大於該配置項時自動開啓重寫。
	auto-aof-rewrite-percentage 100
	
	#當AOF文件增長到一定大小的時候Redis能夠調用 BGREWRITEAOF 對日誌文件進行重寫 。當AOF文件大小大於該配置項時自動開啓重寫
	#一般生產環境會修改此配置,至少3G-5G起,不會64M就重寫一次
	auto-aof-rewrite-min-size 64mb

	#是否開啓混合持久化
	aof-use-rdb-preamble yes

aof重寫機制

of是以日誌追加的方式將命令字符串協議保存在aof 文件中,隨着我們使用redis的時間越長,最redis的操作越多,這個aof文件會越來越大,如果不做處理,總有會撐爆磁盤,所以就出現了重寫,重寫就是專門給aof文件廋身的

他的思想是:直接根據現在內存的數據,生成新的aof文件,然後去替換舊的aof文件,就可以把一下沒用字符去掉,區別我們的mvcc,重寫只關注最終結果,比如set k1 v1 ,然後我們del k1等等一些沒用操作,這樣我們的文件大小就會小很多

當然重寫的觸發時機也是通過配置來設置的,可以參看上面的配置註釋,都有詳細說明
注意:重寫操作是通過fork子進程來完成的,所以正常的aof不會fork子進程,觸發了重寫纔會

那麼問題來了,如果我們redis既開啓了rdb,有開啓了aof,那麼聽誰的?這裏要分兩步來分析
1,運行過程,都起作用,各司其職,很好理解
2,啓動過程,關於啓動過程,我這邊畫了一個啓動時數據加載流程,如下:
在這裏插入圖片描述
解釋一下,redis啓動時數據加載,如果開啓了aof,優先考慮aof,只加載aof文件,沒開起aof則加載rdb文件,肯定有人問爲什麼?通過兩者同步機制便知,rdb執行週期長,數據缺失多,aof執行週期短,數據缺失少,使用默認配置,只會丟失一秒內的數據。所以加載時以aof文件爲主,儘可能保障了停機前數據恢復的完整性!
這點也說明了本文一開始提到的問題2,既然有了rdb爲什麼還要有aof呢?正是因爲rdb執行週期長,所以出現了aof這種持久化機制,儘可能的保障了數據備份的完整性!

那麼可能有人會問?既然aof是一秒同步一次,那麼我將rdb配置成:save 1 1 不也是可以達到一秒同步一次,那跟aof有什麼區別呢?
其實不然,我們前面提到過,rdb是需要fork子線程去執行持久化操作,而aof是直接使用主線程區執行持久化,rdb是備份完整數據,而aof只是保存記錄,在主線程fork子線程的時候會帶來大量的cpu開銷,其實是很消耗性能的,這樣你的redis每時每刻都在做着廢性能的fork子進程操作,redis又談何高性能呢?所以這是不可取的!

redis4.0後混合持久化機制

4.0版本的混合持久化,但是這個設置默認是關閉的,可以通過如下設置開啓我們的混合持久化

	#yes則表示開啓,no表示禁用,5.0之後默認開啓。
	aof-use-rdb-preamble yes

混合持久化是通過bgrewriteaof完成的,不同的是當開啓混合持久化時,fork出的子進程先將共享的內存副本全量的以RDB方式寫入aof文件,然後在將重寫緩衝區的增量命令以AOF方式寫入到文件,寫入完成後通知主進程更新統計信息,並將新的含有RDB格式和AOF格式的AOF文件替換舊的的AOF文件。
簡單的說:新的AOF文件前半段是RDB格式的全量數據後半段是AOF格式的增量數據

優點:混合持久化結合了RDB持久化 和 AOF 持久化的優點, 由於絕大部分都是RDB格式,加載速度快,同時結合AOF,增量的數據以AOF方式保存了,數據更少的丟失。
缺點:兼容性差,一旦開啓了混合持久化,在4.0之前版本都不識別該aof文件,同時由於前部分是RDB格式,二進制文件,閱讀性較差(反正我是看不懂)

性能建議:

上面講完了redis的幾種持久化方式,但是具體到實際生產環境中,改如何去選擇呢?可以參考如下建議:

rdb和aof同時存在時,因爲RDB文件只用作後備用途,只要15分鐘備份一次就夠了,只保留save 900 1這條規則。
重寫:只要硬盤許可,應該儘量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到3G - 5G以上。默認超過原大小100%大小時重寫可以改到適當的數值。

好了關於redis的持久化機制這邊就先講到這裏,如有不當之處,可以留言指正,謝謝!

發佈了47 篇原創文章 · 獲贊 97 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章