Redis學習手冊14—持久化

正如之前所說的,Redis與傳統數據庫的一個主要區別在於,Redis把所有數據都存儲在內存中,而傳統數據庫通常只會把數據的索引存儲在內存中,並將實際的數據存儲在硬盤中。雖然Redis這樣的數據存儲方式使得用戶可以極快的訪問數據,但是由於內存屬於易失存儲器,這對於想把Redis當做數據庫而不僅僅是緩存的用戶來說是不願看到的。

爲了解決上述問題,Redis向用戶提供了持久化功能,這一功能把內存中的數據以文件的形式存儲到硬盤上,即可保證數據不會輕易丟失。

Redis持久化分類

爲了滿足不同的持久化需求,Redis提供了:RDB持久化AOF持久化RDB-AOF混合持久化 等多種持久化方式。

RDB持久化

RDB持久化是Redis默認使用的持久化方式,該功能可以創建出一個經過壓縮的二進制文件,其中包含了服務器在各個數據庫中存儲的鍵值對數據等信息。RDB持久化產生的文件都以 .rdb 後綴結尾。

創建RDB文件

Redis提供了多種創建RDB文件的方法,用戶既可以使用 SAVE 命令或者 BGSAVE命令手動創建RDB文件,也可以通過設置 save 配置選項讓服務器在滿足條件時自動執行 BGSAVE 命令。

SAVE:阻塞服務器並創建RDB文件

使用SAVE命令,要求Redis服務器以同步方式創建一個記錄了服務器當前所有數據庫數據的RDB文件。SAVE 命令是一個無參數命令,它在創建RDB文件成功後返回OK:

redis> SAVE
OK

接收到SAVE命令的Redis服務器將遍歷數據庫包含的所有數據庫,並將各個數據庫包含的鍵值對全部記錄到RDB文件中。在SAVE命令執行期間,Redis服務器被阻塞,直到RDB文件創建完成爲止。如果已有RDB文件,則會使用新的RDB文件替換舊的RDB文件。

BGSAVE:以非阻塞方式創建RDB文件

因爲SAVE命令在執行期間會阻塞服務器,爲了解決這個問題,Redis提供了異步版本 BGSAVE 命令:這個命令會使用子進程來創建RDB文件。

  1. 創建一個子進程。
  2. 子進程執行SAVE命令,創建新的RDB文件。
  3. RDB文件創建完畢之後,子進程退出並通知Redis服務器進程。
  4. Redis服務器進程使用新的RDB文件替換已有的RDB文件。

因爲BGSAVE命令是以異步方式執行的,所以Redis服務器在BGSAVE命令執行期間仍然可以繼續處理其他客戶端發送的命令請求。不過需要注意的是,雖然BGSAVE命令不會像SAVE命令那樣一直阻塞Redis服務器,但由於執行BGSAVE命令需要創建子進程,因此父進程所佔內存越大,數據量越大,創建子進程的過程越慢,因此也有可能會有短暫的阻塞。

通過配置選項自動創建RDB文件

除了可以使用 SAVE命令或者BGSAVE命令之外,還可以通過設置 save 選項,讓Redis服務器在滿足指定條件時自動執行 BGSAVE命令:

save <seconds> <changes>

save 選項接受 secondschanges 兩個參數,前者用於指定觸發持久化所需要的時長,後者則用於指定觸發持久化操作所需要的修改次數。即是說,如果服務器在 seconds 秒之內,對其包含的各個數據庫總共執行了 changes 次修改,那麼服務器就會觸發一次 BGSAVE 命令:

save 60 10000  -- 60s內至少執行了10000次修改

Redis允許用戶同時向服務器提供多個 save 選項,當給定的選項中任意一個條件被滿足時,就會執行一次 BGSAVE 命令

save 6000 1    -- 在6000s(100min)之內,修改了至少 1次
save 600 100   -- 在600s(10min)之內,修改了至少100次
save 60s 10000   -- 在60s(1min)之內,修改了至少 10000次

注意:爲了避免由於同時使用多個觸發條件而導致服務器過於頻繁的執行 BGSAVE 命令,Redis服務器在每次成功創建RDB文件之後,負責自動觸發 BGSAVE 命令的時間計數器以及修改次數計數器都會被清零重新開始計數:無論這個RDB文件是手動創建的還是自動創建的

RDB持久化是默認的持久化方式,如果用戶在啓動Redis時,沒有顯示的關閉RDB持久化功能,也沒有啓用 AOF 持久化功能,那麼Redis默認將使用如下默認配置進行 RDB 持久化:

save 60 10000
save 300 100
save 3600 1

RDB文件結構

在瞭解瞭如何創建RDB文件之後,接下來了解一下RDB文件的具體結構。

總體結構

RDB文件總體結構主要包含7個部分:

  • RDB文件標識符: 文件最開頭的部分爲RDB文件標識符,這個標識符的內容爲 “REDIS” 這5個字符。Redis服務器在嘗試載入RDB文件時,可以通過這個標識符快速的判斷文件是否爲真正的RDB文件。
  • 版本號: 跟在RDB文件標識符之後的是RDB文件的版本號,這個版本號是一個字符串格式的數字,長度爲4個字符。如最新版的:“0009”
  • 設備附加信息: RDB文件的設備附加信息部分記錄了生成RDB文件的Redis服務器及其所在平臺的信息,比如服務器的版本號、宿主機器的架構、創建RDB文件的時間戳、服務器佔用的內存數量等。
  • 數據庫數據: RDB文件的數據庫數據部分記錄了Redis服務器存儲的0個或任意多個數據庫的數據,當這個部分包含多個數據庫時,各個數據庫的數據將按照數據庫號碼從小到大進行排列。
  • Lua腳本緩存: 如果Redis服務器啓用了複製功能,那麼服務器將在RDB文件的Lua腳本緩存部分保存所有已被緩存的Lua腳本。
  • EOF: RDB文件的EOF部分用於標識RDB正文內容的末尾,它實際值爲二進制值 0xFF
  • CRC64校驗和: RDB文件的末尾是一個以無符號64位整數表示的CRC64校驗和,通過這個值可以快速校驗出RDB文件是否有損壞。

數據庫信息結構

RDB文件的數據庫數據部分包含了任意多個數據庫的數據,其中每個數據庫都包含了:

  • 數據庫號碼
  • 鍵值對總數量
  • 帶有過期時間的鍵值對數量
  • 鍵值對數據部分

載入RDB文件

Redis載入RDB文件的步驟如下:

  1. 檢查文件開頭的標識符是否爲 “REDIS”,如果是則繼續執行後續步驟,否則報錯並終止載入操作。

  2. 檢查文件的RDB版本號,以此來判斷當前Redis服務器能否讀取這一版本的RDB文件。

  3. 根據文件中記錄的設備附加信息,執行相應的操作和設置。

  4. 檢查文件的數據庫數據部分是否爲空,如果不爲空就執行以下子操作:
    ① 根據文件記錄的數據庫號碼,切換至正確的數據庫。
    ② 根據文件記錄的鍵值對總量以及帶有過期時間的鍵值對數量,設置數據庫底層數據結構。
    ③ 載入文件記錄的所有鍵值對數據,並在數據庫中重建這些鍵值對。

  5. 如果服務器啓用了複製功能,那麼將之前緩存的Lua腳本重新載入緩存中。

  6. 遇到EOF表示,確認RDB正文內容讀取完畢。

  7. 載入RDB文件末尾的CRC64校驗和,把它與載入數據期間計算出的CRC64校驗和進行對比,以此來判斷被載入的數據是否完好無損。

  8. RDB文件載入完畢,服務器開始接受客戶端請求。

數據丟失

由於RDB持久化方式記錄的是開始執創建文件的那一刻,數據庫中包含的所有數據,這種方式通常也叫做時間點快照。時間快照的特點是:系統在停機時將丟失最後一次成功實施持久化之後的所有數據

因此,對於一個只是用RDB持久化的Redis服務器來說,服務器停機時丟失的數據量將取決於最後一次成功執行RDB持久化操作,以及該操作開始執行的時間。

SAVE命令和BGSAVE命令對於故障停機時的表現也不同:

  • SAVE命令的停機情況
    因爲SAVE命令是一個同步操作,它的開始和結束都位於同一個原子時間內,所以如果用戶使用SAVE命令進行持久化,那麼服務器在停機時將丟失最後一次成功執行SAVE命令之後產生的所有數據。

  • BGSAVE命令的停機情況
    因爲BGSAVE命令是一個異步命令,它的開始和結束並不位於同一個原子時間之內,所以如果用戶採用BGSAVE命令進行持久化,那麼服務器在停機時丟失的數據量將取決於最後一次成功執行的BGSAVE命令的開始時間。

RDB持久化的缺陷

總的來說,無論用戶使用的是SAVE命令還是BGSAVE命令,停機服務器丟失的數據量將取決於創建RDB文件的時間間隔:間隔越長,丟失的數據量越大

矛盾的地方在於,RDB持久化是一種全量持久化操作,它在創建RDB文件時需要存儲整個服務器包含的所有數據,並因此消耗大量的計算資源和內存資源,所以也不太可能通過增大RDB文件的生成頻率來保證數據安全。

RDB持久化的特徵決定了,它更適合用於數據備份,而非普通的數據持久化。

AOF持久化

與全量式的RDB持久化不同,AOF提供的是增量式的持久化功能,這種持久化的核心原理在於:服務器每次執行完寫命令只會,都會以協議文本的方式將被執行的命令追加到AOF文件的末尾。這樣一來,只要重新執行AOF文件中保存的Redis命令,就可以將數據庫恢復至之前的狀態。

打開AOF持久化功能

默認情況下,Redis的AOF持久化功能並未開啓,用戶可以通過 appendonly 選項來決定是否開啓AOF持久化功能:

appendonly yes -- 開啓AOF持久化
appendonly no -- 關閉AOF持久化

設置AOF文件的沖洗頻率

爲了提高程序的寫入性能,通常操作系統會把針對硬盤的多次寫操作優化爲一次寫操作。具體做法是:調用 write 系統對文件進行寫入時,系統並不會直接把數據寫入硬盤,而是會先寫入位於內存中的緩衝區,等到指定的時限到達或滿足某些寫入條件時,纔會執行 flush 系統調用,將緩衝區中的數據寫入硬盤。

這種優化機制雖然提高了程序的性能,但是也給程序的寫入操作帶來了不確定性,特別是對於 AOF 這樣的持久化功能來說,AOF文件的沖洗機制將直接影響 AOF 持久化的安全性。使用 appendfsync 選項可以來控制系統沖洗 AOF 文件的頻率:

appendfsync <value>

appendfsync 選項擁有如下3個值可選:

  • always —— 每執行一個寫命令,就對AOF文件執行一次沖洗操作
  • everysec —— 每隔1s,就對AOF文件執行一次沖洗操作
  • no —— 不主動對AOF文件執行沖洗操作,由操作系統來決定何時執行AOF沖洗操作。

三種不同的沖洗策略不僅會直接影響停機時丟失的數量,還會影響服務器的性能:

  • 在使用 always 的情況下,服務器在停機時最多丟失一個命令的數據,但是頻率太高,會降低Redis服務器的性能。
  • 使用 everysec 的情況下,服務器在停機時最多丟失1s內產生的數據,這是一種兼顧性能和安全的這種方案,也是默認的策略。
  • 使用 no 的情況下,停機時將丟失最後一次沖洗AOF文件之後的所有數據,具體大小則取決於系統對AOF文件沖洗的頻率。

AOF重寫

隨着服務器的不斷運行,被執行的命令越來越多,而負責記錄這些命令的AOF文件也會越來越大。爲了保證AOF文件不會過大,Redis提供了AOF重寫功能,該功能可以生成一個新的AOF文件,並且文件中只包含恢復當前數據庫所需的儘可能少的命令。

BGREWRITEAOF命令

執行BGREWRITEAOF命令可以顯示的觸發AOF重寫操作:

redis> BGREWRITEAOF
Background append only file rewriting started

BGREWRITEAOF命令是一個異步命令,Redis服務器在接收到該命令之後會創建出一個子進程,它會掃描數據庫並生成新的AOF文件。當新的AOF文件生成後,子進程就會退出並通知Redis服務器,然後Redis服務器就會使用新的AOF文件替換舊的AOF文件。

  • 如果用戶發送BGREWRITEAOF命令請求時,服務器正在創建RDB文件,那麼服務器將把AOF重寫操作延後到RDB文件創建完畢之後再執行。
  • 如果服務器在執行重寫操作的過程中,再次收到新的BGREWRITEAOF命令請求,那麼服務器將返回錯誤。

AOF重寫配置選項

除了可以手動執行BGREWRITEAOF命令創建新的AOF文件之外,還可以通過設置以下兩個配置選項讓Redis自動觸發BGREWRITEAOF命令:

auto-aof-rewrite-min-size <value>
auto-aof-rewrite-percentage <value>

其中auto-aof-rewrite-min-size 選項用於設置觸發自動AOF文件重寫所需的最小AOF體積,當AOF文件的體積小於給定值時,服務器不會自動執行BGREWRITEAOF命令,默認情況下該選項值爲:

auto-aof-rewrite-min-size 64mb

也就是說,如果AOF文件的體積小於64MB,那麼Redis將不會自動執行BGREWRITEAOF命令。

auto-aof-rewrite-percentage選項控制的是觸發自動AOF文件重寫所需的文件體積增大比例,默認值如下:

auto-aof-rewrite-percentage 100

表示當前AOF文件的體積比最後一次AOF重寫之後的體積增大了一倍(100%),那麼將自動執行一次BGREWRITEAOF命令。

AOF持久化的優缺點

優點: 與RDB持久化可能會導致大量數據丟失相比,AOF持久化的安全性要高很多:通過使用 everysec選項,用戶可以將數據丟失的時間窗口限制在1s之內。

AOF持久化的缺點如下:

  • 首先,因爲AOF文件存儲的是協議文本,所以它的體積會比包含相同數據的二進制格式的RDB文件要大得多,並且生成AOF文件所需的時間也會比生成RDB文件所需的時間長。
  • 其次,因爲RDB持久化可以直接通過RDB文件恢復數據庫數據,而AOF持久化則需要通過執行AOF文件中保存的命令來恢復數據庫。所以RDB持久化的數據恢復速度將比AOF持久化的速度快的多。
  • 最後,因爲AOF重寫使用的BGREWRITEAOF命令與RDB持久化使用的BGSAVE命令一樣都需要創建子進程,所以在數據庫體積較大的情況下,進行AOF文件重寫將佔用大量資源,並導致服務器被短暫的阻塞。

RDB-AOF混合持久化

爲了兼顧RDB持久化和AOF持久化各自的優點,Redis從4.0版本開始引入了RDB-AOF混合持久化模式,這種模式是基於AOF持久化模式構建而來——如果用戶打開了AOF持久化,並且將

aof-use-rdb-preamble <value>

選項的值設置成了 yes,那麼Redis服務器在執行AOF重寫的時候,就會像執行BGSAVE命令那樣,根據數據庫當前的狀態生成出相應的RDB數據,並將這些數據寫入AOF文件中,至於那些AOF重寫開始之後執行的Redis命令,則會繼續以協議文本的形式追加到新AOF文件的末尾。

也就是說,在開啓了RDB-AOF混合持久化功能後,服務器生成的AOF文件將由兩部分組成,其中位於AOF文件頭部的是RDB格式的數據,而跟在RDB數據後面的則是AOF格式的數據。

關閉持久化

默認情況下,即使用戶沒有顯示的開啓RDB持久化或者AOF持久化,Redis也會默認使用以下配置進行RDB持久化:

save 60 10000
save 300 100
save 3600 1

如果用戶想要徹底關閉這一默認的RDB持久化行爲,那麼可以在Redis服務器啓動時,給出以下配置即可:

save ""

上一篇:Redis學習手冊13—Lua腳本

下一篇:Redis學習手冊15—發佈與訂閱

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章