目錄
- 寫在前面
- 摘要
- Redis持久化介紹
- RDB原理
- Fork函式與寫時復制
- 關于寫時復制的思考
- RDB相關配置
- Fork函式與寫時復制
- AOF原理
- AOF持久化配置
- AOF檔案解讀
- AOF檔案修復
- AOF重寫
- AOF緩沖區與AOF重寫快取區
- AOF緩沖區可以替代AOF重寫緩沖區嗎?
- AOF相關配置
- 寫后日志
- 混合持久化
- 如何選擇合適的持久化方式
- 總結
寫在前面
Redis的持久化,這部分的知識點不僅求職面試的時候是重點,作業中也是經常打交道,說起持久化都會想到RDB和AOF,但是里面有些細節是可以展開去聊的,比如:為什么 fork速度這么快?AOF是如何提高寫入性能的?等問題,對這些疑問本文都會有所解答,
摘要
Redis是許多公司都在使用的一款高性能、非關系型資料庫,其中最為重要的一個特性就是它支持持久化,本文將深入介紹Redis持久化原理,包括RDB和AOF兩種方式的實作,
Redis持久化介紹
Redis持久化分為兩種:RDB(Redis DataBase)和AOF(Append Only File),RDB是指將Redis記憶體中的資料定期寫入磁盤上的一個快照檔案中;而AOF則是以追加的方式記錄Redis執行的每一條寫命令,你也可以同時開啟兩種持久化方式,在這種情況下,當redis重啟的時候會優先載入AOF檔案來恢復原始的資料,
你也許會問,為什么需要持久化呢?因為Redis作為一款記憶體資料庫,在行程例外退出或服務器斷電之后,所有的資料都將消失,如果沒有持久化功能,無法保證資料的持久性,那么這樣的資料庫還有什么用呢?
因此,Redis提供了持久化功能,以確保資料的可靠性和持久性,接下來,我們將分別介紹RDB和AOF的實作原理,
RDB原理
RDB是Redis默認的持久化方式,它將Redis在記憶體中的資料定期寫入到硬碟中,生成一個快照檔案,快照檔案是一個二進制檔案,包含了Redis在某個時間點的所有資料,
RDB的優點是快速、簡單,適用于大規模資料備份和恢復,但是,RDB也有缺點,例如資料可能會丟失,因為Redis只會在指定的時間點生成快照檔案,如果在快照檔案生成之后,但在下一次快照檔案生成之前服務器宕機,那么這期間的資料就會丟失,
由于RDB檔案是以二進制格式保存的,因此它非常緊湊,并且在Redis重啟時可以迅速地加載資料,相比于AOF,RDB檔案一般會更小,
RDB持久化有兩種方式:
- 手動
- 自動
手動方式通過SAVE命令或BGSAVE命令進行,SAVE命令會阻塞Redis服務器,直到快照檔案生成完成,BGSAVE命令會fork一個子行程(注意是子行程,不是子執行緒)在后臺生成快照檔案,不會阻塞Redis服務器,
自動方式則是在組態檔中設定, 讓它在“ N 秒內資料集至少有 M 個改動”這一條件被滿足時, 自動保存一次資料集,
比如說,以下設定會讓 Redis 在滿足 “10秒內有至少100 個鍵被改動” 這一條件時, 自動保存一次資料集,
save 10 100
Fork函式與寫時復制
在 Redis 中,fork 函式被用于創建子行程,Redis 的使用場景中通常有大量的讀操作和較少的寫操作,而 fork 函式可以利用 Linux 作業系統的寫時復制(Copy On Write,即 COW)機制,讓父子行程共享記憶體,從而減少記憶體占用,并且避免了沒有必要的資料復制,
我們可以使用 man fork 來查看下說明檔案,
翻譯:在Linux下,fork()是使用寫時復制的頁面實作的,所以它唯一的代價是復制父行程的頁表以及為子行程創建獨特的任務結構所需的時間和記憶體,
簡單來說就是 fork()函式復制的是指標,不會復制資料,所以速度很快,
需要注意的是,fork的這個程序主行程是阻塞的,fork完之后不阻塞,
在 Redis 中,當執行 RDB 持久化操作時,Redis 會呼叫 fork 函式創建子行程,然后由子行程負責將資料寫入到磁盤中,為了避免父子行程同時對記憶體中的資料進行修改導致資料不一致,Redis 會啟用寫時復制機制,這樣,當父行程修改記憶體中的資料時, Linux 內核會將該部分記憶體復制一份給子行程使用,從而保證父子行程間的資料互相獨立,
簡單畫了個示意圖:
當沒有發生寫的時候,子行程和父行程指向地址是一樣的,發生寫的時候,就會拷貝出一塊新的記憶體區域,實作父子行程隔離,
通過使用 fork 函式和寫時復制機制,Redis 可以高效地執行 RDB 持久化操作,并且不會對 Redis 運行程序中的性能造成太大的影響,同時,這種方式也提供了一種簡單有效的機制來保護 Redis 資料的一致性和可靠性,
缺點:RDB 需要經常fork子行程來保存資料集到硬碟上,當資料集比較大的時候,fork的程序是非常耗時的,可能會導致Redis在一些毫秒級內不能回應客戶端的請求,資料集很大的時候,fork程序可能會持續數秒,
關于寫時復制的思考
上述流程貌似有個問題,比如,有個鍵值對 k1 a ,此時正在bgsave,客戶端發來一個請求,主行程發生寫操作set k1 b,由于寫時復制,此時子行程里k1值還是值a,最終持久化的也是a,
為什么不直接持久化新值而持久化舊值?寫時復制的意義是什么?
主要有2點原因:
- 其實Redis為了性能考慮,將記憶體的資料持久化是一個順序寫的操作,RDB備份是有一個程序的,這個程序是由子行程完成,子行程備份RDB是一個順序寫的程序,如果主行程的所有寫入請求都隨時記錄到RDB檔案中,那么理論更新key可能在任何位置,這是個隨機寫的程序,性能低,
- 其次如果主行程一直在寫入的話,那么這次RDB備份一直都在寫主行程寫入的新值,永遠不會停止,
RDB相關配置
-
save:指定 RDB 持久化操作的條件,當 Redis 的資料發生變化,并且經過指定的時間(seconds)和變化次數(changes)后,Redis 會自動執行一次 RDB 操作,例如,save 3600 10000 表示如果 Redis 的資料在一個小時內發生了至少 10000 次修改,那么 Redis 將執行一次 RDB 操作,
-
stop-writes-on-bgsave-error:指定在 RDB 持久化程序中如果出現錯誤是否停止寫入操作,如果設定為 yes,當 Redis 在執行 RDB 操作時遇到錯誤時,Redis 將停止接受寫入操作;如果設定為 no,Redis 將繼續接受寫入操作,
-
rdbcompression:指定是否對 RDB 檔案進行壓縮,如果設定為 yes,Redis 會在生成 RDB 檔案時對其進行壓縮,從而減少磁盤占用空間;如果設定為 no,Redis 不會對生成的 RDB 檔案進行壓縮,
-
rdbchecksum:指定是否對 RDB 檔案進行校驗和計算,如果設定為 yes,在保存 RDB 檔案時,Redis 會計算一個 CRC64 校驗和并將其追加到 RDB 檔案的末尾;在加載 RDB 檔案時,Redis 會對檔案進行校驗和驗證,以確保檔案沒有受到損壞或篡改,
-
replica-serve-stale-data:這是 Redis 4.0 中新增的一個配置項,用于指定復制節點在與主節點斷開連接后是否繼續向客戶端提供舊資料,當設定為 yes 時,在復制節點與主節點斷開連接后,該節點將繼續向客戶端提供舊資料,直到重新連接上主節點并且同步完全新的資料為止;當設定為 no 時,復制節點會立即停止向客戶端提供資料,并且等待重新連接上主節點并同步資料,需要注意的是,當 replica-serve-stale-data 設定為 yes 時,可能會存在一定的資料不一致性問題,因此建議僅在特定場景下使用,
-
repl-diskless-sync:這是 Redis 2.8 中引入的一個配置項,用于指定復制節點在進行初次全量同步(即從主節點獲取全部資料)時是否采用無盤同步方式,當設定為 yes 時,復制節點將通過網路直接獲取主節點的資料,并且不會將資料存盤到本地磁盤中;當設定為 no 時,復制節點將先將主節點的資料保存到本地磁盤中,然后再進行同步操作,采用無盤同步方式可以避免磁盤 IO 操作對系統性能的影響,但同時也會增加網路負載和記憶體占用,因此,應該根據具體的場景和需求選擇合適的同步方式,
AOF原理
AOF持久化是按照Redis的寫命令順序將寫命令追加到磁盤檔案的末尾,AOF是一種基于日志的持久化方式,它保存了Redis服務器所有寫入操作的日志記錄,以保證資料的持久性、可靠性和完整性,AOF持久化技術的核心思想是將Redis服務器執行的所有寫命令追加到一個檔案中,當Redis服務器重新啟動時,可以通過重新執行AOF檔案來恢復服務器的狀態,
AOF有個比較好的優勢是可以恢復誤操作,舉個例子,如果你不小心執行了 FLUSHALL 命令,但只要 AOF 檔案未被重寫,那么只要停止服務器,移除 AOF 檔案末尾的 FLUSHALL 命令,并重啟 Redis ,就可以將資料集恢復到 FLUSHALL 執行之前的狀態,重寫了就沒辦法了,
AOF持久化配置
Redis的AOF持久化配置頻率可通過appendfsync 引數進行控制,該引數有以下三個選項:
always: 每次有資料修改都立即寫入磁盤,是最安全的選項,everysec: 每秒鐘寫入一次,性能和安全之間做了一個平衡,no: 從不主動寫入,完全依靠作業系統自身的快取機制來決定何時將資料寫入磁盤,
默認情況下,Redis的appendfsync引數設定為everysec,如果需要提高持久化安全性,可以將其改為always;如果更關注性能,則可以將其改為no,但是需要注意的是,使用no可能會導致資料丟失的風險,建議在應用場景允許的情況下謹慎使用,
AOF檔案解讀
這是一個簡單的AOF檔案示例,
- *號:表示引數個數,后面緊跟著引數的長度和值,
- $號:表示引數長度,后面緊跟著引數的值,
實際上AOF檔案中保存的所有命令都遵循相同的格式,即以*開頭表示引數個數,$開頭表示引數長度,其后緊跟著引數的值,
AOF檔案修復
服務器可能在程式正在對 AOF 檔案進行寫入時停機,造成AOF 檔案出錯,
可以使用 Redis 自帶的 redis-check-aof 程式,對原來的 AOF 檔案進行修復:
$ redis-check-aof –fix
AOF重寫
Redis的AOF重寫機制指的是將AOF檔案中的冗余命令洗掉,以減小AOF檔案的大小并提高讀寫性能的程序,
Redis的AOF重寫機制采用了類似于復制的方式,首先將記憶體中的資料快照保存到一個臨時檔案中,然后遍歷這個臨時檔案,只保留最終狀態的命令,生成新的AOF檔案,
具體來說,Redis執行AOF重寫可以分為以下幾個步驟:
- 開始AOF重寫程序,向客戶端回傳一個提示資訊,
- 創建一個臨時檔案,并將當前資料庫中的鍵值對寫入到臨時檔案中,
- 在創建的臨時檔案中將所有的寫命令都轉換成Redis內部的表示格式,即使用一系列的Redis命令來表示一個操作,例如使用SET命令來表示對某個鍵進行賦值操作,
- 對臨時檔案進行壓縮,去掉多余的空格和換行符等,減小檔案體積,
- 將壓縮后的內容寫入到新的AOF檔案中,
- 停止寫入命令到舊的AOF檔案,并將新的AOF檔案的檔案名替換為舊的AOF檔案的檔案名,
- 結束AOF重寫程序,并向客戶端發送完成提示資訊,
通過AOF重寫機制,Redis可以在不停止服務的情況下減小AOF檔案的大小,提高讀寫性能,同時也可以保證資料的一致性,
Redis提供了手動觸發AOF重寫的命令BGREWRITEAOF,可以在Redis的客戶端中執行該命令來啟動AOF重寫程序,Redis 2.2 需要自己手動執行 BGREWRITEAOF 命令; Redis 2.4 則可以自動觸發 AOF 重寫,
具體操作步驟如下:
-
打開redis-cli命令列工具,連接到Redis服務,
-
執行BGREWRITEAOF命令,啟動AOF重寫程序,
$ redis-cli 127.0.0.1:6379> BGREWRITEAOF -
Redis會回傳一個后臺任務的ID,表示AOF重寫任務已經開始,
127.0.0.1:6379> BGREWRITEAOF Background append only file rewriting started by pid 1234 -
可以使用INFO PERSISTENCE命令查看當前AOF檔案的大小和重寫程序的狀態,等待重寫完成即可,
127.0.0.1:6379> INFO PERSISTENCE # Persistence aof_enabled:1 aof_rewrite_in_progress:1 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:0 aof_current_rewrite_time_sec:14 aof_last_bgrewrite_status:ok aof_last_write_status:ok
需要注意的是,執行BGREWRITEAOF命令可能會占用較多的CPU和記憶體資源,因此在生產環境中需要謹慎使用,并確保有足夠的系統資源支持,同時,即使手動觸發AOF重寫,Redis也會在滿足一定條件時自動觸發AOF重寫,以保證AOF檔案的大小和性能,
在版本號大于等于 2.4 的 Redis 中,BGSAVE 執行的程序中,不可以執行 BGREWRITEAOF ,反過來說,在 BGREWRITEAOF 執行的程序中,也不可以執行 BGSAVE,這可以防止兩個 Redis 后臺行程同時對磁盤進行大量的 I/O 操作,
AOF緩沖區與AOF重寫快取區
在Redis中,AOF緩沖區和AOF重寫緩沖區是兩個不同的概念,
AOF緩沖區是一個用于暫存需要寫入AOF檔案的命令的緩沖區,在Redis處理客戶端發來的寫命令時,如果開啟了AOF持久化功能,則該命令將被先寫入到AOF緩沖區,AOF緩沖區中的內容通過配置的規則持久化到磁盤上,持久化規則可以通過配置項appendfsync來調整,
AOF重寫緩沖區是一個用于執行AOF檔案的重寫操作的緩沖區,AOF重寫操作是一種將現有AOF檔案重寫成最小化的新AOF檔案的操作,AOF重寫操作的目的是減少AOF檔案的大小,同時加快恢復速度,AOF重寫快取區在AOF重寫時開始啟用,Redis服務器主行程在執行完寫命令之后,會同時將這個寫命令追加到AOF緩沖區和AOF重寫緩沖區,
需要注意的是,AOF緩沖區和AOF重寫緩沖區是不同的概念,但它們都使用了Redis的記憶體緩沖機制,并且都會影響Redis服務器的性能,因此,在實際使用中,應該合理配置AOF緩沖區大小和AOF重寫規則,以確保Redis服務器的正常運行和高性能,
AOF緩沖區可以替代AOF重寫緩沖區嗎?
AOF緩沖區不可以替代AOF重寫緩沖區的原因是AOF重寫緩沖區記錄的是從重寫開始后的所有需要重寫的命令,而AOF緩沖區可能只記錄了部分的命令(如果寫回的話,AOF快取區的資料就會失效被丟失,因而只會保存一部分的命令,而AOF重寫快取區不會),
AOF緩沖區就主要是Redis用來解決主行程執行命令速度與磁盤寫入速度不同步所設定的,通過AOF緩沖區可以有效地避免頻繁對硬碟進行讀寫,進而提升性能,Redis在AOF持久化的時候,會先把命令寫入到AOF緩沖區,然后通過回寫策略來寫入硬碟AOF檔案,
AOF相關配置
在 Redis 的組態檔 redis.conf 中,可以通過以下配置項來設定 AOF 相關引數:
- appendonly:該配置項用于開啟或關閉 AOF,默認為關閉,若開啟了 AOF,Redis 會在每次執行寫命令時,將命令追加到 AOF 檔案末尾,
- appendfilename:用于設定 AOF 檔案名,默認為 appendonly.aof,
- appendfsync:該配置項用于設定 AOF 的同步機制,有三種可選值:
- always:表示每個寫命令都要同步到磁盤,安全性最高,但是性能較差,
- everysec:表示每秒同步一次,是默認選項,既能保證資料安全,又具有較好的性能,
- no:表示不進行同步,而是由作業系統決定何時將緩沖區中的資料同步到磁盤上,性能最好,但是安全性較低,
- auto-aof-rewrite-percentage和auto-aof-rewrite-min-size:這兩個配置項用于設定 AOF 重寫規則,當 AOF 檔案大小超過 auto-aof-rewrite-min-size 設定的值,并且 AOF 檔案增長率達到 auto-aof-rewrite-percentage 所定義的百分比時,Redis 會啟動 AOF 重寫操作,auto-aof-rewrite-percentage:默認值為100,以及auto-aof-rewrite-min-size:64mb 配置,也就是說默認Redis會記錄上次重寫時的AOF大小,默認配置是當AOF檔案大小是上次rewrite后大小的一倍且檔案大于64M時觸發,
- aof-use-rdb-preamble:Redis 4版本新特性,混合持久化,AOF重寫期間是否開啟增量式同步,該配置項在AOF重寫期間是否使用RDB檔案內容,默認是no,如果設定為yes,在AOF檔案頭加入一個RDB檔案的內容,可以盡可能的減小AOF檔案大小,同時也方便恢復資料,
寫后日志
我們比較熟悉的是資料庫的寫前日志(Write Ahead Log,WAL),也就是說,在實際寫資料前,先把修改的資料記到日志檔案中,以便故障時進行恢復,不過,AOF 日志正好相反,它是寫后日志,“寫后”的意思是 Redis 是先執行命令,把資料寫入記憶體,然后才記錄日志,
why?
為了避免額外的檢查開銷,Redis 在向 AOF 里面記錄日志的時候,并不會先去對這些命令進行語法檢查,所以,如果先記日志再執行命令的話,日志中就有可能記錄了錯誤的命令,Redis 在使用日志恢復資料時,就可能會出錯,
而寫后日志這種方式,就是先讓系統執行命令,只有命令能執行成功,才會被記錄到日志中,否則,系統就會直接向客戶端報錯,所以,Redis 使用寫后日志這一方式的一大好處是,可以避免出現記錄錯誤命令的情況,
除此之外,AOF 還有一個好處:它是在命令執行后才記錄日志,所以不會阻塞當前的寫操作,
不過,AOF 也有兩個潛在的風險,
首先,如果剛執行完一個命令,還沒有來得及記日志就宕機了,那么這個命令和相應的資料就有丟失的風險,如果此時 Redis 是用作快取,還可以從后端資料庫重新讀入資料進行恢復,但是,如果 Redis 是直接用作資料庫的話,此時,因為命令沒有記入日志,所以就無法用日志進行恢復了,
其次,AOF 雖然避免了對當前命令的阻塞,但可能會給下一個操作帶來阻塞風險,這是因為,AOF 日志也是在主執行緒中執行的,如果在把日志檔案寫入磁盤時,磁盤寫壓力大,就會導致寫盤很慢,進而導致后續的操作也無法執行了,
混合持久化
在過去, Redis 用戶通常會因為 RDB 持久化和 AOF 持久化之間不同的優缺點而陷入兩難的選擇當中:
- RDB 持久化能夠快速地儲存和恢復資料,但是在服務器停機時可能會丟失大量資料,
- AOF 持久化能夠有效地提高資料的安全性,但是在儲存和恢復資料方面卻要耗費大量的時間,
為了讓用戶能夠同時擁有上述兩種持久化的優點, Redis 4.0 推出了一個“魚和熊掌兼得”的持久化方案 —— RDB-AOF 混合持久化: 這種持久化能夠通過 AOF 重寫操作創建出一個同時包含 RDB 資料和 AOF 資料的 AOF 檔案, 其中 RDB 資料位于 AOF 檔案的開頭, 它們儲存了服務器開始執行重寫操作時的資料庫狀態,至于那些在重寫操作執行之后執行的 Redis 命令, 則會繼續以 AOF 格式追加到 AOF 檔案的末尾, 也即是 RDB 資料之后,
說就是說開啟混合持久化之后,AOF檔案中的內容:前半部分是二進制的RDB內容,后面跟著AOF增加的資料,AOF位于2次RDB之間,格式類似下面這樣:
(二進制)RDB
AOF
(二進制)RDB
在目前版本中, RDB-AOF 混合持久化功能默認是處于關閉狀態的, 為了啟用該功能, 用戶不僅需要開啟 AOF 持久化功能, 還需要將 aof-use-rdb-preamble 選項的值設定為 true,
appendonly yes
aof-use-rdb-preamble yes
如何選擇合適的持久化方式
當你想選擇適合你的應用程式的持久化方式時,你需要考慮以下幾個因素:
- 資料的實時性和一致性:如果您對資料的實時性和一致性有很高的要求,則AOF可能是更好的選擇,
- 如果您對資料的實時性和一致性要求不太高,并且希望能快速地加載資料并減少磁盤空間的使用,那么RDB就可能更適合您的應用程式,因為RDB檔案是二進制格式的,所以它很緊湊,并且在Redis重啟時可以迅速地加載資料,
- Redis的性能需求:如果您對Redis的性能有很高的要求,那么關閉持久化功能也是一個選擇,因為持久化功能可能會影響Redis的性能,但是一般不建議這么做,
總結
通過本文的介紹,我們了解了Redis持久化的兩種方式,RDB和AOF,它們都有自己獨特的優勢,我們應該根據專案大小、資料量和業務需求制定不同的持久化策略,
本篇文章就到這里,感謝閱讀,如果本篇博客有任何錯誤和建議,歡迎給我留言指正,文章持續更新,可以關注公眾號第一時間閱讀,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548443.html
標籤:Java
