【趙渝強老師】Redis的RDB資料持久化

赵渝强老师發表於2024-11-07

Redis是記憶體資料庫,如果不將記憶體中的資料庫狀態儲存到磁碟,那麼一旦伺服器程序退出會造成伺服器中的資料庫狀態也會消失。所以 Redis 提供了資料持久化功能。Redis支援兩種方式的持久化,一種是RDB方式;另一種是AOF(append-only-file)方式。兩種持久化方式可以單獨使用,也可以將這兩種方式結合使用。
  
影片講解如下:
https://www.bilibili.com/video/BV1LrvwejEbq/?aid=112867328133...

這裡重點討論一下Redis的RDB資料持久化。RDB持久化是Redis預設的持久化方式。它是指在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,實際操作過程是fork一個子程序,先將資料集寫入臨時檔案,寫入成功後再替換之前的檔案,並用二進位制壓縮儲存。
  
影片講解如下:
https://www.bilibili.com/video/BV1qKv6e9E3h/?aid=112873485371...

一、RDB持久化機制的工作流程

RDB執行快照的時機由以下引數決定:

# Save the DB to disk.
# save <seconds> <changes>
# Redis will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
# Snapshotting can be completely disabled with a single empty
# string argument as in following example:
# save ""
# Unless specified otherwise, by default Redis will save the DB:
# * After 3600 seconds (an hour) if at least 1 key changed
# * After 300 seconds (5 minutes) if at least 100 keys changed
# * After 60 seconds if at least 10000 keys changed
# You can set these explicitly by uncommenting the three following lines.
# save 3600 1
# save 300     100
# save 60     10000

提示:Redis執行RDB是透過save命令實現的。在預設情況下觸發RDB的條件如下:
# save 3600     1        在3600秒內,如有1個Key發生了變化,則執行RDB。
# save 300    100        在300秒內,如有100個Key發生了變化,則執行RDB。
# save 60     10000    在60秒內,如有1萬個Key發生了變化,則執行RDB。

RDB持久化機制的工作流程如下:
(1)Redis根據配置引數去生存rdb快照檔案
(2)Redis將fork一個子程序出來。
(3)由子程序嘗試將記憶體中的資料dump到臨時的rdb檔案中。
(4)完成rdb快照檔案的生成之後,就會去替換舊的快照檔案。

從RDB的工作流程可以看出,RDB具有以下的優點和缺點:

  • 適合大規模的資料恢復。
  • 如果業務對資料完整性和一致性要求不高,RDB是很好的選擇。
  • 資料的完整性和一致性不高,因為RDB可能在最後一次備份時當機了。
  • 備份時佔用記憶體,因為Redis 在備份時會獨立建立一個子程序,將資料寫入到一個臨時檔案,最後再將臨時檔案替換之前的備份檔案。所以要考慮到大概兩倍的資料膨脹性。

Redis監控RDB最直接的方法當然就是使用系統提供的info命令來做了。只需要執行下面一條命令,就能獲得Redis關於RDB的狀態報告。

bin/redis-cli info | grep rdb_

輸出的資訊如下:
rdb_changes_since_last_save:0     表明上次RDB儲存以後改變的鍵的個數。
rdb_bgsave_in_progress:0         表示當前是否在進行RDB操作,0表示沒有進行。
rdb_last_save_time:1650184060     上次執行RDB操作的時間戳。
rdb_last_bgsave_status:ok         上次執行RDB操作的狀態
rdb_last_bgsave_time_sec:-1     上次執行RDB操作的耗時。
rdb_current_bgsave_time_sec:-1     目前執行RDB操作已花費的時間。
rdb_last_cow_size:0             表示父程序與子程序比較執行了多少修改操作。

二、剖析RDB持久化機制

在rdb.c檔案中可以找到建立RDB檔案的函式rdbSave(),函式定義如下:

/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
int rdbSave(char *filename, rdbSaveInfo *rsi) {
    ......
    // 建立臨時檔案
    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    ......
    // 初始化I/O
    rioInitWithFile(&rdb,fp);
    // 開始執行RDB
    startSaving(RDBFLAGS_NONE);
    ......
    //如果持久化成功操作成功,則用臨時檔案替代舊的檔案
    if (rename(tmpfile,filename) == -1) {
        char *cwdp = getcwd(cwd,MAXPATHLEN);
        serverLog(LL_WARNING,
        "Error moving temp DB file %s on the final "
        "destination %s (in server root dir %s): %s",
        tmpfile,
        filename,
        cwdp ? cwdp : "unknown",
        strerror(errno));
        unlink(tmpfile);
        stopSaving(0);
        return C_ERR;
    }
    serverLog(LL_NOTICE,"DB saved on disk");
    //持久化成功後,將計數器重置為0,並更新最近儲存時間。
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = C_OK;
    stopSaving(1);
    return C_OK;
    ......
}

相關文章