大家好,我是小羽。

Redis 的讀寫都是在記憶體中進行的,所以它的效能高。而當我們的伺服器斷開或者重啟的時候,資料就會消失,那麼我們該怎麼解決這個問題呢?

其實 Redis 已經為我們提供了一種持久化的機制,分別是 RDBAOF 兩種方式,接下來跟著我一起看看這兩個錦囊都是怎麼保證資料的持久化的。

持久化

由於 Redis 是基於記憶體的資料庫,所以當伺服器出現故障的時候,我們的資料就得不到安全保障。

這個時候就需要將記憶體中的資料儲存到磁碟中,當我們伺服器重啟時,便可以通過磁碟來恢復資料,這個過程就叫做 Redis 持久化。

Redis持久化

RDB

簡介

RDB全稱Redis Database Backup file(Redis資料備份檔案),也可以稱為Redis資料快照

  • RDB 檔案是一個經過壓縮的二進位制檔案(預設:dump.rdb);

  • RDB 檔案儲存在硬碟裡;

  • 通過儲存資料庫中的鍵值對來記錄資料庫狀態。

建立

當 Redis 持久化時,程式會將當前記憶體中的資料庫狀態儲存到磁碟中。

建立

建立 RDB 檔案主要有兩個 Redis 命令:SAVEBGSAVE

SAVE

同步操作,執行命令時,會阻塞 Redis 伺服器程序,拒絕客戶端傳送的命令請求。

程式碼示例

def SAVE():
    # 建立 RDB 檔案
    rdbSave()

圖示

Save命令

BGSAVE

非同步操作,執行命令時,子程序執行儲存工作,伺服器還可以繼續讓主執行緒處理客戶端傳送的命令請求。

程式碼示例

def BGSAVE():
    # 建立子程序
    pid = fork()     if pid == 0:
        # 子程序負責建立 RDB 檔案
        rdbSave()
        # 完成之後向父程序傳送訊號
        signal_parent()
    elif pid > 0:
        # 父程序繼續處理命令請求,並通過輪訓等待子程序的訊號
        handle_request_and_wait_signal()
    else:
        handle_fork_error()

圖示

bgSave命令

載入

載入工作在伺服器啟動時自動執行。

載入

伺服器在載入 RDB 檔案期間,會一直處於阻塞狀態,直到載入工作完成為止。

主要設定

Redis 允許使用者通過設定伺服器配置的 save 選項,讓伺服器每隔一段時間自動執行一次 BGSAVE 命令。

設定儲存條件

提供配置如下:

save 900 1
save 300 10

在這種情況下,只要滿足以下條件中的一個,BGSAVE 命令就會被執行:

  • 伺服器在 900 秒之內,對資料庫進行了至少 1 次修改了;

  • 伺服器在 300 秒之內,對資料庫進行了至少 10 次修改。

saveparams

伺服器程式會根據 save 選項所設定的儲存條件,設定伺服器狀態 redisServer 結構的 saveparams 屬性。

  • saveparams 屬性是一個數組;

  • 陣列中的每一個元素都是一個 saveparam 結構;

  • 每個 saveparam 結構都儲存了一個 save 選項設定的儲存條件。

struct saveparam {
    // 秒數
    time_t seconds;
    // 修改數
    int changes;
}
dirty

dirty 計數器記錄距離上一次成功執行 SAVE 命令或 BGSAVE 命令之後,伺服器對資料庫狀態進行了多少次修改(包括寫入、刪除、更新等操作)。

lastsave

是一個 UNINX 時間戳,記錄了伺服器上一次成功執行 SAVE 命令或者 BGSAVE 命令的時間。

檢查儲存條件是否滿足

伺服器週期性操作函式 serverCron (該函式對正在執行的伺服器進行維護)預設每隔 100 毫秒就會執行一次,其中一項工作就是檢查 save 選項所設定的儲存條件是否已經滿足,滿足的話就執行 BGSAVE 命令。

程式碼示例

def serverCron():
    # ....
    # 遍歷所有儲存條件
    for saveparam in server.saveparams:
        # 計算距離上次執行儲存操作有多少秒
        save_interval = unixtime_now() - server.lastsave         # 如果資料庫狀態的修改次數超過條件所設定的次數
        # 如果距離上次儲存的時間超過條件所設定的時間
        if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
            BGSAVE()

預設配置

RDB 檔案預設的配置如下:

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#在給定的秒數和給定的對資料庫的寫運算元下,自動持久化操作。
#   save <seconds> <changes>

save 900 1
save 300 10
save 60 10000 #bgsave發生錯誤時是否停止寫入,一般為yes
stop-writes-on-bgsave-error yes #持久化時是否使用LZF壓縮字串物件?
rdbcompression yes #是否對rdb檔案進行校驗和檢驗,通常為yes
rdbchecksum yes # RDB持久化檔名
dbfilename dump.rdb #持久化檔案儲存目錄
dir ./

AOF

簡介

AOF全稱為 Append Only File(追加日誌檔案)。日誌是寫後日志,Redis 是先執行命令,把資料寫入記憶體,然後才記錄日誌。

寫後日志

  • 通過儲存 Redis 伺服器所執行的寫命令來記錄資料庫狀態;

  • 寫入 AOF 檔案的所有命令都是以 Redis 的命令請求協議格式儲存的。

實現

AOF 持久化流程實現主要是通過以下流程來實現的:

AOF流程

命令追加

若 AOF 持久化功能處於開啟狀態,伺服器在執行完一個命令後,會以協議格式將被執行的寫命令追加到伺服器狀態的 aof_buf 緩衝區的末尾。

檔案同步

伺服器每次結束一個事件迴圈之前,都會呼叫 flushAppendOnlyFile 函式,這個函式會考慮是否需要將 aof_buf 緩衝區中的內容寫入和儲存到 AOF 檔案裡。

flushAppendOnlyFile 函式執行以下流程:

  • WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案;

  • SAVE:根據條件,呼叫 fsync 或 fdatasync 函式,將 AOF 檔案儲存到磁碟中。

這個函式是由伺服器配置的 appendfsync 的三個值:always、everysec、no 來影響的,也被稱為三種策略。

Always

每條命令都會 fsync 到硬碟中,這樣 redis 的寫入資料就不會丟失。

Always

everysec

每秒都會重新整理緩衝區到硬碟中(預設值)。

everysec

no

根據當前作業系統的規則決定什麼時候重新整理到硬碟中,不需要我們來考慮。

no

資料載入

  1. 建立一個不帶網路連線的偽客戶端;

  2. 從 AOF 檔案中分析並讀取出一條寫命令;

  3. 使用偽客戶端執行被讀出的寫命令;

  4. 一直執行步驟 2 和 3,直到 AOF 檔案中的所有寫命令都被處理完畢為止。

檔案重寫

為何需要檔案重寫:

  • 為了解決 AOF 檔案體積膨脹的問題;

  • 通過重寫建立一個新的 AOF 檔案來替代現有的 AOF 檔案,新的 AOF 檔案不會包含任何浪費空間的冗餘命令。

實現

檔案重寫的實現原理:

  • 不需要對現有的 AOF 檔案進行任何操作;

  • 從資料庫中直接讀取鍵現在的值;

  • 用一條命令記錄鍵值對,從而代替之前記錄這個鍵值對的多條命令。

後臺重寫

為不阻塞父程序,Redis 將 AOF 重寫程式放到子程序裡執行。

在子程序執行 AOF 重寫期間,伺服器程序需要執行三個流程:

  1. 執行客戶端發來的命令;

  2. 將執行後的寫命令追加到 AOF 緩衝區;

  3. 將執行後的寫命令追加到 AOF 重寫緩衝區。

伺服器流程

預設配置

AOF 檔案預設的配置如下:

############################## APPEND ONLY MODE ###############################

#開啟AOF持久化方式
appendonly no #AOF持久化檔名
appendfilename "appendonly.aof"
#每秒把緩衝區的資料fsync到磁碟
appendfsync everysec
# appendfsync no
#是否在執行重寫時不同步資料到AOF檔案
no-appendfsync-on-rewrite no # 觸發AOF檔案執行重寫的增長率
auto-aof-rewrite-percentage 100
#觸發AOF檔案執行重寫的最小size
auto-aof-rewrite-min-size 64mb #redis在恢復時,會忽略最後一條可能存在問題的指令
aof-load-truncated yes #是否開啟混合開關
aof-use-rdb-preamble yes

總結

通過以上的簡介,想必大家都對 Redis 持久化有了大致的瞭解,那麼這兩種方式,我們該如何選擇呢?

  • 對於大中型的應用,我們既想保證資料完整性又想保證高效率,就應該結合使用 RDB 和 AOF 兩種方式;

  • 如果只是需要保證資料的完整性,保護資料不會丟失,那麼優先使用 AOF 方式;

  • 如果是處理大規模的資料恢復,追求更高更快的效率的話,優先使用 RDB 方式。

也可以參照下圖進行選擇:

主要對比