你真的瞭解Redis的持久化機制嗎?
我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。
2022年6月的某一天,辦公區的氣氛格外的壓抑,此時的小編吹著空調雙眼微眯的站在同事胖虎身後,看著他滿頭大汗的將資料庫的資料匯入到Redis。
經過小編的一番詢問才知道,原來是他們生產Redis今天莫名其妙的掛了,而且因為是新專案,一會要給領導演示用,生產環境Redis就只部署了一個節點,這一掛不要緊,快取進去的資料怎麼辦...
恰巧這個時候,小編聽見老闆辦公室裡傳出了一聲大喊,“胖虎,來給我演示一下專案”,五分鐘後,滿臉通紅的胖虎從老闆辦公室裡出來了。
這時小編突然想到,Redis不是有持久化機制嗎? 在和胖虎的交談中發現,他竟然不知道Redis的持久化機制,小編就默默地裝了個13。
Redis持久化
Redis 的持久化機制有兩種,一種是快照(RDB),另一種是 AOF 日誌。RDB是一次全量備份,AOF 日誌是連續的增量備份。快照是記憶體資料的二進位制序列化形式,在儲存上非常緊湊,而 AOF 日誌記錄的是記憶體資料修改的指令記錄文字。
RDB快照
RDB快照持久化是Redis預設開啟的,它會根據配置策略將記憶體資料儲存在一個名為dmp.rdb的二進位制檔案中。
在redis.conf配置檔案中,可以配置RDB的持久化策略,系統預設是有三個儲存策略(三個同時生效),如下:
- save 900 1 900秒內有1個鍵發生改變
- save 300 10 30秒內有10個鍵發生改變
- save 60 10000 60秒內有10000個鍵發生改變
我們也可以根據我們自己生產環境的具體情況進行配置。如果我們要關閉RDB快照,直接將配置檔案中上述三個save註釋掉即可。
寫時複製機制(COW)
Redis提供了save命令進行快照生成,但是save命令要阻塞主程序(執行客戶端命令的執行緒),當需要生成快照的記憶體特別大時(幾百個G),需要的時間也會很長,甚至十幾秒,所以這個時候如果阻塞主程序,會導致整個服務不可用,就得不償失了。
針對這種情況,Redis藉助作業系統提供的寫時複製技術,提供了bgsave命令,basave命令可以在主程序的基礎上,fork一個子程序,子程序會共享主程序的程式碼段和資料段,相當於是在後臺生成快照。
在bgsave子程序寫入RDB資料時,如果主程序對這些資料也都是讀操作,那麼,主程序和 bgsave 子程序相互不影響。但是,如果主程序要修改一塊資料,這塊資料就會被複制一份,生成該資料的副本。然後,bgsave 子程序會把這個副本資料寫入RDB,而在這個過程中,主程序仍然可以直接修改原來的資料。
演示
bgsave會進行後臺操作,不阻塞流程。 儲存後會生成一個dump.rdb檔案。因為二進位制檔案開啟也是亂碼,所以我們就不打開了。
優點
- Redis宕機後資料恢復快
- 二進位制檔案體積小
缺點
- 持久化策略可能會導致在宕機時資料丟失量較多
AOF(append-only file)
AOF持久化可以通過redis.conf檔案中的appendonly引數控制是否開啟。
- appendonly no 預設情況下是no, 將no改為yes即可開啟AOF持久化方式,Redis會將修改的每一條指令先記錄到系統快取,預設每隔一秒鐘就把快取重新整理到appendonly.aof磁碟檔案中 當 Redis 重新啟動時, 程式就可以通過重新執行 AOF 檔案中的命令來達到重建資料集的目的。
演示
小編直接修改redis.conf檔案中的appendonly引數,然後重啟redis,set了幾個鍵值對,等了好幾秒,發現當前目錄下並沒有生成appendonly.aof檔案。
後來通過直接在redis-cli執行config set appendonly yes命令,當前目錄下才生成appendonly.aof檔案。
接下來我們開啟appendonly.aof檔案就能看到我們之前寫入的鍵值對。
*2
$6
SELECT
$1
0
*3
$3
SET
$1
1
$1
2
*3
$3
SET
$4
key1
$4
val1
*3
$3
SET
$4
3124
$3
124
*3
$3
SET
$5
key14
$4
key2
這是一種resp協議格式資料,星號後面的數字代表命令有多少個引數,$號後面的數字代表這個引數有幾個字元。
AOF持久化策略
- appendfsync always:每次有新命令追加到 AOF 檔案時就執行一次 fsync ,非常慢,也非常安全,不建議用,本身Redis就是藉助的記憶體快的優勢,要是每次操作都訪問磁碟這個優勢就沒了。
- appendfsync everysec:每秒 fsync 一次,足夠快,並且在故障時只會丟失 1 秒鐘的資料,AOF預設會走在這個配置。
- appendfsync no:從不 fsync ,將資料交給作業系統來處理。更快,也更不安全的選擇,發生宕機時,丟失的資料最多。
AOF重寫
假設我們執行了兩個命令 set key1 1 與 set key1 2,在開啟AOF持久化的情況下,這兩個命令都會被記錄到AOF檔案中,但是我們在宕機後重啟恢復資料的時候是不是就只需要執行set key1 2就可以了,之前的set key1 1在恢復資料的時候忽略掉也沒有任何影響,並且因為AOF檔案隨著時間的推移,體積會越來越大,重寫AOF就顯得尤為重要。
手動觸發後臺重寫,redis客戶端執行命令:bgrewriteaof
Redis提供了兩個配置引數控制AOF自動重寫頻率(redis.conf)。 - auto‐aof‐rewrite‐min‐size 64mb //aof檔案至少要達到64M才會自動重寫,檔案太小恢復速度本來就 很快 - auto‐aof‐rewrite‐percentage 100 //aof檔案自上一次重寫後文件大小增長了100%則再次觸發重寫
觸發重寫時 Redis 會通過fork主程序,生成一個子程序去做,這麼做的原因是因為重寫一般意味著要有大量的I/O操作,非常耗時,如果用主程序去重寫會導致主程序長時間的阻塞,影響Redis執行客戶端命令,甚至導致服務長時間不可用,但是子程序去重寫就意味著主程序仍然可以接收、處理客戶端指令,就會導致重寫後的AOF檔案和伺服器當前的資料庫狀態不一致。
例如:子程序fork主程序時,記憶體中只有key1這個鍵,但是當開始重寫後,客戶端又多set了key2、key3,這就導致了重寫的AOF檔案和資料庫不一致。
所以Redis在AOF重寫時加了一個AOF重寫緩衝區,當有新命令執行的時候,Redis會將命令傳送到AOF緩衝區和AOF重寫緩衝區。
執行完重寫任務後,子程序會向主程序傳送一個訊號,主程序收到訊號後會執行下面兩個操作(以下兩個操作會阻塞主程序): - 將 AOF 重寫緩衝區中的所有內容寫入到新的 AOF 檔案中,保證新 AOF 檔案儲存的資料庫狀態和伺服器當前狀態一致。 - 對新的 AOF 檔案進行改名,原子的覆蓋現有 AOF 檔案,完成新舊檔案的替換
雖然只有上面兩個操作會阻塞Redis主程序,但是很顯然它已經將重寫對效能的影響降到了最低。但是如果你想再對其進行優化,這裡有兩個解決方案。
- 服務低峰期定時重寫
- 使用ssd,提升持久化效率
注意:AOF在重寫時,為了避免執行命令時造成的客戶端緩衝區溢位,重寫程式在處理列表、雜湊表、集合、有序集合這四種可能會有多個元素的鍵時,會先檢查鍵所包含的元素數量,如果元素的數量超過了redis.h/REDIS_AOF_REWRITe_ITeMS_PER_CMD常量的值(預設64個元素),那麼重寫程式將會使用多條命令來記錄鍵的值,而不是單單隻用一條命令。
優點
- 資料完整性要高於RDB,預設情況下,最多丟失1秒鐘的資料。
缺點
- Redis宕機後資料恢復慢
- 檔案體積大
混合持久化(sence redis4.0)
Redis宕機恢復時,為了提高資料的完整程度,我們通常使用 AOF 日誌進行恢復,但是使用 AOF 日誌效能相對 RDB來說要慢很多,這樣在 Redis 例項很大的情況下,啟動需要花費很長的時間。 Redis4.0 為了解決這個問題,推出了混合持久化,說白了,混合持久化就是結合了RDB與AOF兩種持久化方式的優勢。
啟混合模式後,AOF在重寫時會將AOF檔案中的資料以RDB檔案的二進位制格式寫入當前AOF檔案中,之後的新的寫操作繼續以AOF檔案的格式進行追加。當redis宕機重啟的時候,載入 AOF 檔案進行恢復資料:先載入 RDB 的部分再載入剩餘的 AOF內容,因此重啟效率大幅得到提升。
通過修改redis.conf檔案中的以下配置開啟混合持久化(必須先開啟AOF持久化):
- aof‐use‐rdb‐preamble yes
總結
- RDB檔案會儲存Redis中所有的鍵值對資料
- save生成RDB檔案會阻塞主程序
- bgsave命令後臺生成RDB,不會阻塞主程序
- RDB檔案體積小,儲存的是二進位制資料
- RDB通過配置策略執行,可能會丟失部分資料
- RDB檔案在Redis宕機後恢復快
- AOF檔案儲存的是客戶端執行的命令
- AOF檔案體積大,恢復慢,但是預設配置下資料比RDB要全很多
- Redis宕機重啟後預設是用AOF方式進行恢復
- AOF檔案中的命令是以Resp協議格式儲存的
- 命令會先儲存到緩衝區,再定期同步到AOF檔案
- AOF會根據配置策略自動的對AOF檔案進行重寫,以降低檔案體積
- AOF重寫時會fork一個子程序去執行,同時會有一個重寫緩衝區,用來儲存重寫時主程序修改的鍵
- AOF重寫時,最後會生成一個新的AOF檔案,覆蓋原有的檔案
- 將重寫緩衝區的內容寫入到AOF與替換新舊AOF檔案時會阻塞主程序
- 混合持久化是RDB與AOF的優勢結合所產生的
- 混合持久化本質還是用的AOF檔案
- 混合持久化使用的前提是開啟AOF持久化
- 混合持久化在重寫AOF檔案時會將資料直接寫成RDB的二進位制格式,之後新的命令還是以AOF檔案Resp協議格式進行儲存
附
fsync
為了提高檔案的寫入效率,linux系統在做持久化時,會呼叫write函式,先將要寫入檔案的資料儲存在一個記憶體緩衝區裡就會直接返回,等到緩衝區被填滿,或者超過了指定的時限(一般是30秒)後,才會將緩衝區的資料寫入到磁碟。
這種做法雖然提高了寫入效率,但是如果發生宕機,就有可能會導致緩衝區中還未寫入磁碟的資料丟失。
為此,linux提供了fsync函式,它會強制並阻塞等待系統將資料寫入到磁碟,從而保證資料的安全性
子程序
為什麼Redis會fork一個子程序而不用子執行緒,是因為子程序可以攜帶主程序的資料副本,可以避免在不使用鎖的情況下,保證資料安全。
注:子程序在重寫期間,主程序還是會接收並處理客戶端命令,會導致子程序與主程序資料不一致。
參考資料
Redis的設計與實現
微信搜一搜:雲下風瀾
首發文章、系列文章連載閱讀