一、前言
我们在上一篇文章聊到了 redis 通过 aof 机制确保了数据的可靠性,但是 aof 日志记录的其实是操作过的命令,并不是实际数据本身,通过 aof 日志进行恢复需要消耗较多的时间,因为每个命令都要执行一遍。这个时间基本上和 aof 日志的大小成正比,如果数据量不大,那倒是无所谓,但是如果数据量特别大,恢复数据的时间可就长了,那么 redis 的团队难道就没考虑到这种情况吗?当然考虑到了,所以才有了 RDB 机制。
二、什么是 COW 机制
写时复制技术,实际上是linux 系统提供的一种内存优化手段,简单来说,就是在不修改源数据的情况下,并不会真正的去开辟新的内存空间,而是相关的变量引用仍然指向同一块内存区域,只有复制的变量数据发生变化时,才会开辟新空间,大大节约了内存的使用。
目前已经广泛运用在 PHP,Java 等语言的设计上。
三、RDB 机制是什么?
RDB 就是 Redis DataBase 的缩写,对 redis 来说,它是一种快照记录的方式,记录的是某一个时刻的数据,而不是操作。
以下是相关的配置文件说明:
#
# Save the DB on disk:
# save <seconds> <changes>
# save "" 或者 注释掉所有的 save 可以关闭此功能
# 实际上下面的都是执行的 basave 命令
save 900 1 # 表示在900秒内至少有1个key 发生了变化则出发RDB机制
save 300 10 # 表示在300秒内至少有10个key 发生了变化则出发RDB机制
save 60 10000 # 表示在60秒内有至少有10个key 发生了变化则出发RDB机制
#bgsave 出错时停止写入
stop-writes-on-bgsave-error yes
# 是否使用LZF压缩字符串对象,默认为 yes
rdbcompression yes
# 是否开启校验和检查
rdbchecksum yes
# rdb 的文件名
dbfilename dump.rdb
由于是数据快照,所以在恢复数据的时候,redis 可以将 RDB 文件读入内存来快速恢复数据,因此 redis 在实现快照的时候采取的是全量数据快照的方式 。
所谓的快照,可以简单的理解为给账本进行拍照存储。
因此我们需要考虑几个因素,照片谁来拍,多久拍一次,拍的时候账本还能动吗?
四、谁来拍照 ?
由于 Redis 是单线程的,理论上我们应该尽量避免会阻塞主线程的相关操作,因为这是十分致命的,因此 redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave。
- save:在主线程中执行,会导致阻塞;
- bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。
127.0.0.1:6379> save
OK
127.0.0.1:6379> bgsave
Background saving started
五、拍的时候账本还能动吗?
首先,很明显账本当然是可以动的,如果不能动的话,主线程不就阻塞了,这是不能容忍的,但是如果账本可以动的话,使用 RDB 进行全量数据快照的时候会不会出问题呢。
在执行 bgsave 命令的时候,会基于主线程 fork 出一个 bgsave 子进程,因此 bgsave 子进程共享了主线程的所有内存数据,并开始将它们写入 rdb 文件中。
此时,主线程如果接收了修改这些数据的一些命令,那么这块数据就会被复制一份,生成对应的数据副本。然后主线程直接在这些副本数据上进行处理。而 bgsave 子进程就继续读取原来的数据写入文件中,这也是利用了操作系统提供的 cow 技术,数据发生改变再开辟新空间存数据,不改就指向原来的数据。
好比说,老板让财务帮忙进行拍照统计,财务开始工作后,如果有数据修改更改,财务会复制一份对应的数据到另一个账本里面,然后老板可以去另一个账本进行修改,这样不会影响到财务拍照了。
六、多久拍一次比较合适?
财务干活也需要时间,如果只依赖 RDB 机制,那么每两次拍照的间隔有变化的数据就是可能会丢失的最新数据。那么是不是这个间隔越短丢失的数据风险就越小呢?想的真美好,不把财务当人了吗?这不得累死他们。
我们需要考虑两个原因,假设每次快照的时间间隔很短,会有两个影响。
第一个是fork 的阻塞问题,每次主线程 fork 出 bgsave 进程的时候是会阻塞主线程的。
第二个是CPU的问题,高频率的每次全量数据写入磁盘,会有很多的 IO,虽然 redis 保证了存活的 bgsave 进程只能同时存在一个,但是仍然无法保证 CUP 过高的问题。
此时有人会说,那就不要全量了,我们改成增量怎么样?
假设我们第一次进行全量的写入,后续都改用增量的怎么样,只要改变的key ,我们才进行处理。
想法很美好,但是考虑到每个key 你都要做标记处理,额外的内存空间支出,就让人感觉有点亏了。毕竟内存,那么的贵。
七、两种机制混合使用
简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
因为 AOF 日志记录的是命令,因此日志的大小以及恢复数据的速度较慢。
而 RDB 恢复的数据快,却需要考虑两次快照间隔的数据变化问题。
那么进行两种方式的结合,就可以解决彼此的问题。恢复又快,AOF 又能解决 RDB 的间隔数据变化问题。
实际上就是,在每次间隔中,将相关的写命令记录在 AOF 日志中,在下次快照完成后,又可以清空旧的 AOF 。
八、总结
这篇文章我们讲了Redis 持久化机制的另一个方式 RDB , 它可以通过快照的方式快速恢复数据,通过结合 AOF 日志 可以更好的实现又快又稳定的恢复机制,基于这两种机制,Redis 也可以实现持久化存储。从而提高了其使用的方便性。不过在日常的工作中,我们仍然不能直接把 redis 当做永久存储来用。非关系数据库相比于关系型数据库,还是有着很多不方便的地方,如无法提供银行级别的事务机制,以及内存和磁盘的性价比等问题。