天天看點

Redis 源碼分析——RBD 持久化,面試官被我講懵了

作者:網際網路進階架構師

原理

Redis 提供了 RDB 持久化功能,這個功能可以将 Redis 在記憶體中的資料庫狀态儲存到磁盤裡面,避免資料意外丢失。

觸發時機:手動觸發、自動觸發。

配置讀取

redis.conf中 rdb 相關的配置如下:

rdbcompression 
  rdb 檔案為了解約空間,支援壓縮,要開啟該功能
  需要在配置檔案中設定參數 rdbcompression (預設開啟的),
  目前開啟參數後 redis 利用 lz 算法對
stop-writes-on-bgsave-error
  為了保證資料的一緻性,redis 預設開啟該選項
dbfilename
  rdb 檔案名,預設 dump.rdb
save <seconds> <changes>
  指明 rdb 觸發機制,表示 seconds 秒改變 changes 觸發           

mac 系統 brew 安裝 rdb 檔案位置:

Redis 源碼分析——RBD 持久化,面試官被我講懵了

rdb 檔案檢視

od -c dump.rdb

➜  redis od -c dump.rdb
0000000    R   E   D   I   S   0   0   0   9 372  \t   r   e   d   i   s
0000020    -   v   e   r 005   6   .   0   .   9 372  \n   r   e   d   i
0000040    s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 313
0000060  313   x   '   b 372  \b   u   s   e   d   -   m   e   m 302 300
0000100    B 020  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
0000120    e 300  \0 376  \0 373 001  \0 370   "  \0 003   m   s   g 300
0000140  001 377   9   3 235 231   R 251   \   N                        
0000152           

手動觸發

Redis 可以執行 SAVE、BGSAVE進行手動觸發 RDB 持久化操作。

  • SAVE 指令會阻塞 Redis 服務程序,直到 RDB 檔案常見完畢。
  • BGSAVE 不會阻塞伺服器程序,會建立一個子程序,子程序負責建立 RDB 檔案。

RDB 檔案的建立是由 rdb.c/rdbSave函數完成,具體代碼如下:

/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
int rdbSave(char *filename, rdbSaveInfo *rsi) {
    char tmpfile[256];
    char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
    FILE *fp = NULL;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        char *cwdp = getcwd(cwd,MAXPATHLEN);
        serverLog(LL_WARNING,
            "Failed opening the RDB file %s (in server root dir %s) "
            "for saving: %s",
            filename,
            cwdp ? cwdp : "unknown",
            strerror(errno));
        return C_ERR;
    }

    rioInitWithFile(&rdb,fp);
    startSaving(RDBFLAGS_NONE);

    if (server.rdb_save_incremental_fsync)
        rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);

    if (rdbSaveRio(&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp)) goto werr;
    if (fsync(fileno(fp))) goto werr;
    if (fclose(fp)) { fp = NULL; goto werr; }
    fp = NULL;
    
    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    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");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = C_OK;
    stopSaving(1);
    return C_OK;

// 出錯處理
werr:
    serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    if (fp) fclose(fp);
    unlink(tmpfile);
    stopSaving(0);
    return C_ERR;
}           

自動觸發

Redis 支援使用者通過設定伺服器配置save 選項(在 redis.conf 中) ,讓服務伺服器每間隔一段事件自動執行一次 BGSAVE指令。

預設配置如下所示:

save 900 1
save 300 10
save 60 10000           

那麼隻要滿足以下三個條件中的任意一個,BGSAVE指令就會被執行:

  • 伺服器在 900 秒之内,對資料庫進行了至少 1 次修改。
  • 伺服器在 300 秒之内,對資料庫進行了至少 10 次修改。
  • 伺服器在 60 秒之内,對資料庫進行了至少 10000 次修改。

伺服器中會根據 save 選項設定的儲存條件,設定伺服器狀态的 redisServer 結構的 saveparams 屬性

struct redisServer {

	// 記錄儲存條件
    struct saveparam *saveparams;   /* Save points array for RDB */
}

// saveparam 定義
struct saveparam {
    time_t seconds;
    int changes;
};           

總結

1、Redis 中提供了手動,自動兩種方式來持久化資料,其實也是在性能和可靠性的折中處理。

2、自動持久化除了 saveparams數組之外,伺服器狀态還維持着一個dirty計數器,以及一個lastsave屬性:

  • dirty 計數器記錄距離上一次成功執行 SAVE 指令或者 BGSAVE 指令之後,伺服器對資料庫狀态(伺服器中的所有資料庫)進行了多少次修改(包括寫入、删除、更新等操作)。
  • lastsave 屬性是一個UNIX時間戳,記錄了伺服器上一次成功執行 SAVE 指令或者 BGSAVE 指令的時間。
struct redisServer {

    // 修改計數器
    long long dirty;                /* Changes to DB from the last save */
    
    // 上次儲存時間
    time_t lastsave;                /* Unix time of last successful save */
}           

3、Redis 的伺服器周期性操作函數 serverCron 預設每隔 100 毫秒就會執行一次,該函數用于對正在運作的伺服器進行維護,它的其中一項工作就是檢查 save 選項所設定的儲存條件是否已經滿足,如果滿足的話,就執行 BGSAVE 指令。

RDB 結構圖

圖例:

  1. 粉色框中字段為固定字元串
  2. 綠色框中字段為整數
  3. 紫色框中是拓展的資料
  4. 黃色框中value類型是一個枚舉的常量
  5. 藍色框中是實際存儲的鍵值對資料
Redis 源碼分析——RBD 持久化,面試官被我講懵了

作者:心城以北

連結:https://juejin.cn/post/7072757775247343623

來源:稀土掘金

繼續閱讀