前言
Redis 是內存數據庫,它將自己的數據儲存在內存里面,如果不想辦法將儲存在內存中的數據保存到磁盤里面,那么一旦服務器進程退出,服務器中的數據也就沒了。
因此,Redis 提供了 RDB 持久化功能,這個功能可以將 Redis 在內存中的數據保存到磁盤里面,避免丟失。
問題描述
有兩臺計算機,分別為 A、B(A 是本機,B 是云服務器)。兩臺計算機操作系統版本,Redis 版本完全相同。
我有一些數據儲存在 A 的 Redis 中(B 無法直接獲取這些數據),我想把這些數據保存到 B 中。于是在 A 中調用 SAVE 命令生成 rdb 文件,將 rdb 文件拷貝到 B,想讓 B 加載這個 rdb 文件。
rdb 文件的加載工作是在服務器啟動時自動執行的,于是我在 B 執行以下命令:
sudo systemctl restart redis
可是,B 的 Redis 中并沒有我預期的數據,還是 B 之前的數據。只要重啟后,我拷貝到 B 的 dump.rdb 文件就沒了,就又被替換成之前的文件了。
經過排查,B 并沒有開啟 AOF 持久化,redis.conf 配置的 dump.rdb 文件名和路徑都沒有問題。排查日志發現 rdb 文件加載正常,并沒有打印錯誤信息。
問題解決
經過重重排查,終于發現了問題,問題出在 Redis 關閉的時候。
server.c/prepareForShutdown 函數的片段如下:
int prepareForShutdown(int flags) {int save = flags & SHUTDOWN_SAVE;int nosave = flags & SHUTDOWN_NOSAVE;/* Create a new RDB file before exiting. */if ((server.saveparamslen > 0 && !nosave) || save) {serverLog(LL_NOTICE,"Saving the final RDB snapshot before exiting.");/* Snapshotting. Perform a SYNC SAVE and exit */rdbSaveInfo rsi, *rsiptr;rsiptr = rdbPopulateSaveInfo(&rsi);if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {serverLog(LL_WARNING,"Error trying to save the DB, can't exit.");return C_ERR;}}
}
準備關閉的時候,在 server.saveparamslen > 0 時,有可能會調用 rdbSave(server.rdb_filename,rsiptr) 生成 rdb 文件。
我們可以在 redis.conf 中設置一些條件讓 Redis 自動執行 BGSAVE 命令,例如:
save 900 1
save 300 10
save 60 10000
server.saveparamslen 就是我們設置的 BGSAVE 保存條件數組的長度,在這個例子中為 3。
于是問題就很清晰了,在 Redis 關閉的時候,它生成了一分 rdb 文件,替換了我拷貝到 B 的 rdb 文件,于是啟動的時候加載的也是它剛生成的這份。
只要我先關閉 Redis,再將 dump.rdb 拷貝到 B,再在 B 啟動 Redis 就可以解決了。
參考資料
- 《Redis 設計與實現》
- Redis 5.0.8 server.c/prepareForShutdown