为什么 Redis 快照使用子进程 (一)

2019 年 12 月 26 日

为什么 Redis 快照使用子进程 (一)

为什么这么设计(Why’s THE Design)是一系列关于计算机领域中程序设计决策的文章,我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题,可以在文章下面留言。


虽然我们经常将 Redis 看做一个纯内存的键值存储系统,但是我们也会用到它的持久化功能,RDB 和 AOF 就是 Redis 为我们提供的两种持久化工具,其中 RDB 就是 Redis 的数据快照,我们在这篇文章想要分析 Redis 为什么在对数据进行快照持久化时会需要使用子进程,而不是将内存中的数据结构直接导出到磁盘上进行存储。


概述


在具体分析今天的问题之前,我们首先需要了解 Redis 的持久化存储机制 RDB 究竟是什么,RDB 会每隔一段时间中对 Redis 服务中当下的数据集进行快照,除了 Redis 的配置文件可以对快照的间隔进行设置之外,Redis 客户端还同时提供两个命令来生成 RDB 存储文件,也就是 SAVEBGSAVE,通过命令的名字我们就能猜出这两个命令的区别。



其中 SAVE 命令在执行时会直接阻塞当前的线程,由于 Redis 是 单线程 的,所以 SAVE 命令会直接阻塞来自客户端的所有其他请求,这在很多时候对于需要提供较强可用性保证的 Redis 服务都是无法接受的。


我们往往需要 BGSAVE 命令在后台生成 Redis 全部数据对应的 RDB 文件,当我们使用 BGSAVE 命令时,Redis 会立刻 fork 出一个子进程,子进程会执行『将内存中的数据以 RDB 格式保存到磁盘中』这一过程,而 Redis 服务在 BGSAVE 工作期间仍然可以处理来自客户端的请求。


rdbSaveBackground 就是用来处理在后台将数据保存到磁盘上的函数:


C


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 服务的可用性,但是到了这里我们其实能够发现两个问题:


  1. 为什么 fork 之后的子进程能够获取父进程内存中的数据?

  2. fork 函数是否会带来额外的性能开销,这些开销我们怎么样才可以避免?


既然 Redis 选择使用了 fork 的方式来解决快照持久化的问题,那就说明这两个问题已经有了答案,首先 fork 之后的子进程是可以获取父进程内存中的数据的,而 fork 带来的额外性能开销相比阻塞主线程也一定是可以接受的,只有同时具备这两点,Redis 最终才会选择这样的方案。


设计


为了分析上一节提出的两个问题,我们在这里需要了解以下的这些内容,这些内容是 Redis 服务器使用 fork 函数的前提条件,也是最终促使它选择这种实现方式的关键:


  1. 通过 fork 生成的父子进程会共享包括内存空间在内的资源;

  2. fork 函数并不会带来明显的性能开销,尤其是对内存进行大量的拷贝,它能通过写时拷贝将拷贝内存这一工作推迟到真正需要的时候;


本文转载自 Draveness 技术博客。


原文链接:https://draveness.me/whys-the-design-redis-bgsave-fork


2019 年 12 月 26 日 17:27270

评论

发布
暂无评论
发现更多内容

工作总结

Arthur

JAVA已过气?中俄大佬对话告诉你俄罗斯最受欢迎的编程语言是什么!

华为云开发者社区

Java 开源 程序员 Lambda 编程语言

你只加了两行代码,为什么要花两天时间?

Yukun

程序员 debug bug

秒懂云通信:如何用阿里云平台发短信?

巨侠说

架构师第七周学习总结

小蚂蚁

Kubernetes 1.0 发布刚六周年,IBM 却想招 12 年经验的

神经星星

程序员 Kubernetes 云原生 招聘 ibm

性能测试

考尔菲德

MySQL常用函数

Bruce Duan

mysql常用函数

一周信创舆情观察(7.13~7.19)

统小信uos

数据库 舆情 芯片

作业1

东哥

极客大学架构师训练营

通过双 key 来解决缓存并发问题

Bruce Duan

缓存穿透 缓存并发 双key解决缓存并发

总结

ruettiger

架构师训练第七周

邵帅

【DevCloud·敏捷智库】如何利用故事点做估算

华为云开发者社区

敏捷 敏捷开发 需求 故事 华为云

性能压测工具

武鹏

架构师训练第七周总结

邵帅

《深度工作》学习笔记(1)

石云升

读书笔记 专注 深度工作

架构师训练营第七周作业

Bruce Xiong

第7章总结

武鹏

全国第一枚企业区块链电子印章诞生

CECBC区块链专委会

萝卜章 区块链印章 全流程上链 e签宝

SpringBoot分布式验证码登录方案

Bruce Duan

验证码 Kaptcha

计算机网络基础(九)---网络层-内部网关路由协议

书旅

计算机网络 网络协议 操作系统 计算机基础

【研报下载】InfoQ《2020中国技术发展白皮书》重磅发布

InfoQ写作平台

写作平台 InfoQ 白皮书 研究报告 活动专区

阿里官方 Redis 开发规范

Bruce Duan

redis Redis开发规范

第7周作业一

ruettiger

报销流程太慢太复杂?区块链技术引入票据系统效率翻一倍

CECBC区块链专委会

数据共享 电子票据 优化业务 可信体系

【week07】总结

chengjing

【week07】作业

chengjing

架构师训练营作业-web性能压测示例代码

superman

极客大学架构师训练营

埋点全解析,你最关心的可视化埋点在这里!

易观大数据

架构师培训第七周练习

小蚂蚁

为什么 Redis 快照使用子进程 (一)-InfoQ