🚄【Redis 干货领域】从底层彻底吃透 RDB 原理(基础篇)

每日一句
低头是一种能力,它不是自卑,也不是怯弱,它是清醒中的嬗变。有时,稍微低一下头,或者我们的人生路会更精彩。
前提概要
Redis 是一个的键-值(K-V)对的内存数据库服务,通常包含了任意个非空数据库。而每个非空的键值数据库中又可
以存放任意个 K-V,基本的结构如下图所示:

Redis 服务器的结构
这里有一个问题,因为 Redis 是一个内存数据库,如果它直接将数据存储到内存中,但是如果不考虑将存储在内存中的数据持久化到硬盘里面,一旦服务器进程退出,那么数据库中的数据也会消失。
数据库的持久化机制主要有两种,一种是 RDB 机制,另外一种是 AOF 机制,AOF 机制已经在前面的文章中介绍过了,
如果有兴趣可以去看看,而本文主要讲述 RDB 机制。
RDB 机制
Redis 提供了 RDB 持久化能力,这个功能可以将 Redis 在内存中的数据库状态保持在磁盘里面,避免数据意外丢失。
RDB 持久化机制可以手动执行,也可以根据服务器配置选定定期执行操作,该功能可以将某一个时间点
的数据快照进行保存到一个 RDB 文件中。
Redis 服务器的结构
这里有一个问题,因为 Redis 是一个内存数据库,如果它直接将数据存储到内存中,但是如果不考虑将存储在内存中的数据持久化到硬盘里面,一旦服务器进程退出,那么数据库中的数据也会消失。
数据库的持久化机制主要有两种,一种是 RDB 机制,另外一种是 AOF 机制,AOF 机制已经在前面的文章中介绍过了,
如果有兴趣可以去看看,而本文主要讲述 RDB 机制。
RDB 持久化方式
RDB 持久化是指在指定的时间间隔内将 redis 内存中的数据集快照写入磁盘,实现原理是 redis 服务在指定的时间间隔内先 fork 一个子进程,由子进程将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储,生成 dump.rdb 文件存放在磁盘中。

RDB 机制
Redis 提供了 RDB 持久化能力,这个功能可以将 Redis 在内存中的数据库状态保持在磁盘里面,避免数据意外丢失。
RDB 持久化机制可以手动执行,也可以根据服务器配置选定定期执行操作,该功能可以将某一个时间点的数据快照进行保存到一个 RDB 文件中。
RDB 优势
一旦采用该方式,那么你的整个 Redis 数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近 24 小时的数据,同时还要每天归档一次最近 30 天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
对于灾难恢复而言,RDB 是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
性能最大化。对于 Redis 的服务进程而言,在开始持久化时,它唯一需要做的只是 fork 出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行 IO 操作了。
相比于 AOF 机制,如果数据集很大,RDB 的启动效率会更高。
RDB 劣势
如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么 RDB 将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。
RDB 配置规则
在 redis 的 6379.conf 配置文件中:
备份配置参数
save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。官方出厂配置默认是 900 秒内有 1 个更改,300 秒内有 10 个更改以及 60 秒内有 10000 个更改,则将内存中的数据快照写入磁盘。
文件配置参数
默认的 rdb 文件路径是当前目录,文件名是 dump.rdb,可以在配置文件中修改路径和文件名,分别是 dir 和 dbfilename.
压缩配置参数
在进行镜像备份时,是否进行压缩。
如果没有触发自动快照,需要对 Redis 执行手动快照操作,save 和 bgsave 命令来手动快照,两个命令是:
SAVE:由主进程进行快照,会阻塞其他请求。
BGSAVE:通过 fork 子进程进行快照,不会阻塞其他请求。
注意:由于 Redis 使用 fork 来复制一份当前进程,那么子进程就会占有和主进程一样的内存资源,比如说主进程 8G 内存,那么在备份的时候,必须保证有 16G 的内存,要不然会启用虚拟内存,性能非常的差。
快照的过程如下:
Redis 使用 fork 函数复制一份当前进程(父进程)的副本(子进程);
父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
当子进程写入完所有数据后会用该临时文件替换旧的 RDB 文件,至此一次快照操作完成。(注意:会存在写一部命令压缩缓存区,记录写入 rdb 文件时候的操作)
在执行 fork 的时候操作系统会使用写时复制(copy-on-write)策略,即 fork 函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时(如执行一个写命令),操作系统会将该片数据复制一份以保证子进程的数据不受影响,所以新的 RDB 文件存储的是执行 fork 时那一刻的内存快照数据。
通过上述过程可以发现 Redis 在进行快照的过程中不会修改 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候 RDB 文件都是完整的。这使得可以通过定时备份 RDB 文件来实现 Redis 数据库备份。
快照的过程压缩分析:
RDB 文件是经过压缩(上文介绍了:可以配置 rdbcompression 参数以禁用压缩节省 CPU 占用)的二进制格式,所以占用的空间会小于内存中的数据大小,更加利于传输。
快照的读取加载过程:
Redis 启动后会读取 RDB 快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将一个记录一千万个字符串类型键、大小为 1GB 的快照文件载入到内存中需要花费 20~30 秒钟。
通过 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用 AOF 方式进行持久化。
RDB 的优缺点
优点:
适合大规模的数据恢复。
如果业务对数据完整性和一致性要求不高,RDB 是很好的选择。
缺点:
数据的完整性和一致性不高,因为 RDB 可能在最后一次备份时宕机了。
备份时占用内存,因为 Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍),最后再将临时文件替换之前的备份文件。
由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。(回写和覆盖的时候用的是主进程)。
RDB 与 AOF 二者选择的标准(虽然还没有讲 AOF,提前普及)
如果系统是愿意牺牲一些性能,换取更高的缓存一致性(aof)
或者是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行 save 的时候,再做备份(rdb)。
Redis 允许同时开启 AOF 和 RDB,既保证了数据安全又使得进行备份等操作十分容易。此时重新启动 Redis 后 Redis 会使用 AOF 文件来恢复数据,因为 AOF 方式的持久化可能丢失的数据更少。
总结
Redis 默认开启 RDB 持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。
RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。
Redis 需要手动开启 AOF 持久化方式,默认是每秒将写操作日志追加到 AOF 文件中。
所以 Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
版权声明: 本文为 InfoQ 作者【李浩宇/Alex】的原创文章。
原文链接:【http://xie.infoq.cn/article/f7e510874a892d7bb644ed186】。文章转载请联系作者。
评论