RDB 会每隔一段时间中对 Redis 服务中当下的数据集进行快照,除了 Redis 的配置文件可以对快照的间隔进行设置之外,Redis 客户端还同时提供两个命令来生成 RDB 存储文件,也就是 SAVE
和 BGSAVE
。
其中 SAVE
命令在执行时会直接阻塞当前的线程,由于 Redis 是 单线程 的,所以 SAVE
命令会直接阻塞来自客户端的所有其他请求,这在很多时候对于需要提供较强可用性保证的 Redis 服务都是无法接受的。
我们往往需要 BGSAVE
命令在后台生成 Redis 全部数据对应的 RDB 文件,当我们使用 BGSAVE
命令时,Redis 会立刻 fork
出一个子进程,子进程会执行『将内存中的数据以 RDB 格式保存到磁盘中』这一过程,而 Redis 服务在 BGSAVE
工作期间仍然可以处理来自客户端的请求。
rdbSaveBackground
就是用来处理在后台将数据保存到磁盘上的函数:
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
pid_t childpid;
if (hasActiveChildProcess()) return C_ERR;
...
if ((childpid = redisFork()) == 0) {
int retval;
/* Child */
redisSetProcTitle("redis-rdb-bgsave");
retval = rdbSave(filename,rsi);
if (retval == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_RDB, "RDB");
}
exitFromChild((retval == C_OK) ? 0 : 1);
} else {
/* Parent */
...
}
...
}
Redis 服务器会在触发 BGSAVE
时调用 redisFork
函数来创建子进程并调用 rdbSave
在子进程中对数据进行持久化。
使用 fork
的目的最终一定是为了不阻塞主进程来提升 Redis 服务的可用性。
参考Linux写时拷贝
https://edgar615.github.io/linux-cow.html
Redis中的写时复制
- Redis在持久化时,如果是采用BGSAVE命令或者BGREWRITEAOF的方式,那Redis会fork出一个子进程来读取数据,从而写到磁盘中。
- 总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。
- 而在rehash阶段上,写操作是无法避免的。所以Redis在fork出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
参考资料
https://mp.weixin.qq.com/s/PJ1-D9XK3pd7fWUUpm4FyQ