原理
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 檔案位置:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIjBXPt9mcm9zZuBnL0MmNwIzNkZmZwMWMxQTYhRmMyQjZ3gDNykjYxgTM5QzLcBza5QTcsJja2FXLp1ibj1ycvR3Lc5Wanlmcv9CXt92YucWbp9WYpRXdvRnLzA3Lc9CX6MHc0RHaiojIsJye.png)
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 結構圖
圖例:
- 粉色框中字段為固定字元串
- 綠色框中字段為整數
- 紫色框中是拓展的資料
- 黃色框中value類型是一個枚舉的常量
- 藍色框中是實際存儲的鍵值對資料
作者:心城以北
連結:https://juejin.cn/post/7072757775247343623
來源:稀土掘金