内容简介:点击上方"你的关注意义重大!Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。但是一旦进程退出,Redis 的数据就会丢失。
点击上方" 程序员历小冰 ",选择“置顶或者星标”
你的关注意义重大!
Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。但是一旦进程退出,Redis 的数据就会丢失。
为了解决这个问题,Redis 提供了 RDB 和 AOF 两种持久化方案,将内存中的数据保存到磁盘中,避免数据丢失。
antirez 在《Redis 持久化解密》一文中说,一般来说有三种常见的策略来进行持久化操作,防止数据损坏:
-
方法1 是数据库不关心发生故障,在数据文件损坏后通过数据备份或者快照来进行恢复。Redis 的 RDB 持久化就是这种方式。
-
方法2 是数据库使用操作日志,每次操作时记录操作行为,以便在故障后通过日志恢复到一致性的状态。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况。类似于 Mysql 的 redo 和 undo 日志,具体可以看这篇 《InnoDB的磁盘文件及落盘机制》 文章。
-
方法3 是数据库不进行老数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了。CouchDB就是此做法的优秀范例。
RDB 就是第一种方法,它就是把当前 Redis 进程的数据生成时间点快照( point-in-time snapshot ) 保存到存储设备的过程。
RDB 的使用
RDB 触发机制分为使用指令手动触发和 redis.conf 配置自动触发。
手动触发 Redis 进行 RDB 持久化的指令的为:
-
save ,该指令会阻塞当前 Redis 服务器,执行 save 指令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止。
-
bgsave,执行该命令时,Redis 会在后台异步执行快照操作,此时 Redis 仍然可以相应客户端请求。具体操作是 Redis 进程执行
fork
操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。Redis 只会在fork
期间发生阻塞,但是一般时间都很短。但是如果 Redis 数据量特别大,fork
时间就会变长,而且占用内存会加倍,这一点需要特别注意。
自动触发 RDB 的默认配置如下所示:
如果不需要 Redis 进行持久化,那么可以注释掉所有的 save 行来停用保存功能,也可以直接一个空字符串来停用持久化:save ""。
Redis 服务器周期操作函数 serverCron
默认每个 100 毫秒就会执行一次,该函数用于正在运行的服务器进行维护,它的一项工作就是检查 save 选项所设置的条件是否有一项被满足,如果满足的话,就执行 bgsave 指令。
RDB 整体流程
了解了 RDB 的基础使用后,我们要继续深入对 RDB持久化的学习。在此之前,我们可以先思考一下如何实现一个持久化机制,毕竟这是很多中间件所需的一个模块。
首先,持久化保存的文件内容结构必须是紧凑的,特别对于数据库来说,需要持久化的数据量十分大,需要保证持久化文件不至于占用太多存储。其次,进行持久化时,中间件应该还可以快速地响应用户请求,持久化的操作应该尽量少影响中间件的其他功能。最后,毕竟持久化会消耗性能,如何在性能和数据安全性之间做出平衡,如何灵活配置触发持久化操作。
接下来我们将带着这些问题,到源码中寻求答案。
本文中的源码来自 Redis 4.0 ,RDB持久化过程的相关源码都在 rdb.c 文件中。其中大概的流程如下图所示。
上图表明了三种触发 RDB 持久化的手段之间的整体关系。通过 serverCron
自动触发的 RDB 相当于直接调用了 bgsave 指令的流程进行处理。而 bgsave 的处理流程启动子进程后,调用了 save 指令的处理流程。
下面我们从 serverCron
自动触发逻辑开始研究。
自动触发 RDB 持久化
如上图所示, redisServer
结构体的 save_params
指向拥有三个值的数组,该数组的值与 redis.conf 文件中 save 配置项一一对应。分别是 save9001
、 save30010
和 save6010000
。 dirty
记录着有多少键值发生变化, lastsave
记录着上次 RDB 持久化的时间。
而 serverCron
函数就是遍历该数组的值,检查当前 Redis 状态是否符合触发 RDB 持久化的条件,比如说距离上次 RDB 持久化过去了 900 秒并且有至少一条数据发生变更。
如果符合触发 RDB 持久化的条件, serverCron
会调用 rdbSaveBackground
函数,也就是 bgsave 指令会触发的函数。
子进程后台执行 RDB 持久化
执行 bgsave 指令时,Redis 会先触发 bgsaveCommand
进行当前状态检查,然后才会调用 rdbSaveBackground
,其中的逻辑如下图所示。
rdbSaveBackground
函数中最主要的工作就是调用 fork
命令生成子流程,然后在子流程中执行 rdbSave
函数,也就是 save 指令最终会触发的函数。
为什么 Redis 使用子进程而不是线程来进行后台 RDB 持久化呢?主要是出于Redis性能的考虑,我们知道Redis对客户端响应请求的工作模型是单进程和单线程的,如果在主进程内启动一个线程,这样会造成对数据的竞争条件。所以为了避免使用锁降低性能,Redis选择启动新的子进程,独立拥有一份父进程的内存拷贝,以此为基础执行RDB持久化。
但是需要注意的是,fork 会消耗一定时间,并且父子进程所占据的内存是相同的,当 Redis 键值较大时,fork 的时间会很长,这段时间内 Redis 是无法响应其他命令的。除此之外,Redis 占据的内存空间会翻倍。
生成 RDB 文件,并且持久化到硬盘
Redis 的 rdbSave
函数是真正进行 RDB 持久化的函数,它的大致流程如下:
-
首先打开一个临时文件,
-
调用
rdbSaveRio
函数,将当前 Redis 的内存信息写入到这个临时文件中, -
接着调用
fflush
、fsync
和fclose
接口将文件写入磁盘中, -
使用
rename
将临时文件改名为 正式的 RDB 文件, -
最后记录
dirty
和lastsave
等状态信息。这些状态信息在serverCron
时会使用到。
这里要简单说一下 fflush
和 fsync
的区别。它们俩都是用于刷缓存,但是所属的层次不同。 fflush
函数用于 FILE*
指针上,将缓存数据从应用层缓存刷新到内核中,而 fsync
函数则更加底层,作用于文件描述符,用于将内核缓存刷新到物理设备上。
关于 Linux IO 的具体原理可以参考《聊聊Linux IO》
内存数据到 RDB 文件
rdbSaveRio
会将 Redis 内存中的数据以相对紧凑的格式写入到文件中,其文件格式的示意图如下所示。
rdbSaveRio
函数的写入大致流程如下:
-
先写入 REDIS 魔法值,然后是 RDB 文件的版本( rdb_version ),额外辅助信息 ( aux )。辅助信息中包含了 Redis 的版本,内存占用和复制库( repl-id )和偏移量( repl-offset )等。
-
然后
rdbSaveRio
会遍历当前 Redis 的所有数据库,将数据库的信息依次写入。 先写入RDB_OPCODE_SELECTDB
识别码和数据库编号,接着写入RDB_OPCODE_RESIZEDB
识别码和数据库键值数量和待失效键值数量,最后会遍历所有的键值,依次写入。 -
在写入键值时,当该键值有失效时间时,会先写入
RDB_OPCODE_EXPIRETIME_MS
识别码和失效时间,然后写入键值类型的识别码,最后再写入键和值。 -
写完数据库信息后,还会把 Lua 相关的信息写入,最后再写入
RDB_OPCODE_EOF
结束符识别码和校验值。
rdbSaveRio
在写键值时,会调用 rdbSaveKeyValuePair
函数。该函数会依次写入键值的过期时间,键的类型,键和值。
根据键的不同类型写入不同格式,各种键值的类型和格式如下所示。
Redis 有庞大的对象和数据结构体系,它使用六种底层数据结构构建了包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象的对象系统。感兴趣的同学可以参考 《十二张图带你了解 Redis 的数据结构和对象系统》 一文。
不同的数据结构进行 RDB 持久化的格式都不同。我们今天只看一下集合对象是如何持久化的。
后记
欢迎大家持续订阅,后续将会有更加优秀的文章奉上。
推荐阅读
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Redis持久化存储详解(一)
- Redis AOF 持久化详解
- hibernate教程--持久化类状态详解
- hibernate教程--持久化类状态详解
- Redis 专题:万字长文详解持久化原理
- 有效避免数据丢失!Redis持久化方案选择详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
利用Python进行数据分析 原书第2版
Wes McKinney / 徐敬一 / 机械工业出版社 / 2018-7 / 119
本书由Python pandas项目创始人Wes McKinney亲笔撰写,详细介绍利用Python进行操作、处理、清洗和规整数据等方面的具体细节和基本要点。第2版针对Python 3.6进行全面修订和更新,涵盖新版的pandas、NumPy、IPython和Jupyter,并增加大量实际案例,可以帮助你高效解决一系列数据分析问题。 第2版中的主要更新包括: • 所有的代码,包括把Py......一起来看看 《利用Python进行数据分析 原书第2版》 这本书的介绍吧!