一、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;
    ......
}