共计 3011 个字符,预计需要花费 8 分钟才能阅读完成。
大家好,我是小林。
虽说 Redis 是内存数据库。
然而它为数据的长久化提供了两个技术,别离是「AOF 日志和 RDB 快照」。
这两种技术都会用各用一个日志文件来记录信息,然而记录的内容是不同的。
- AOF 文件的内容是操作命令;
- RDB 文件的内容是二进制数据。
对于 AOF 长久化的原理我在上一篇曾经介绍了,明天次要讲下 RDB 快照。
所谓的快照,就是记录某一个霎时货色,比方当咱们给风光拍照时,那一个霎时的画面和信息就记录到了一张照片。
所以,RDB 快照就是记录某一个霎时的内存数据,记录的是理论数据,而 AOF 文件记录的是命令操作的日志,而不是理论的数据。
因而在 Redis 复原数据时,RDB 复原数据的效率会比 AOF 快些,因为间接将 RDB 文件读入内存就能够了,不须要像 AOF 那样还须要额定执行操作命令的步骤能力复原数据。
接下来,就来具体聊聊 RDB 快照。
快照怎么用?
要相熟一个货色,先看看怎么用是比拟好的形式。
Redis 提供了两个命令来生成 RDB 文件,别离是 save
和 bgsave
,他们的区别就在于是否在「主线程」里执行:
- 执行了 save 命令,就会在主线程生成 RDB 文件,因为和执行操作命令在同一个线程,所以如果写入 RDB 文件的工夫太长,会阻塞主线程;
- 执行了 bgsava 命令,会创立一个子过程来生成 RDB 文件,这样能够 防止主线程的阻塞;
RDB 文件的加载工作是在服务器启动时主动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。
Redis 还能够通过配置文件的选项来实现每隔一段时间主动执行一次 bgsava 命令,默认会提供以下配置:
save 900 1
save 300 10
save 60 10000
别看选项名叫 sava,实际上执行的是 bgsava 命令,也就是会创立子过程来生成 RDB 快照文件。
只有满足下面条件的任意一个,就会执行 bgsava,它们的意思别离是:
- 900 秒之内,对数据库进行了至多 1 次批改;
- 300 秒之内,对数据库进行了至多 10 次批改;
- 60 秒之内,对数据库进行了至多 10000 次批改。
这里提一点,Redis 的快照是 全量快照,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。
所以能够认为,执行快照是一个比拟重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,失落的数据会更多。
通常可能设置至多 5 分钟才保留一次快照,这时如果 Redis 呈现宕机等状况,则意味着最多可能失落 5 分钟数据。
这就是 RDB 快照的毛病,在服务器产生故障时,失落的数据会比 AOF 长久化的形式更多,因为 RDB 快照是全量快照的形式,因而执行的频率不能太频繁,否则会影响 Redis 性能,而 AOF 日志能够以秒级的形式记录操作命令,所以失落的数据就绝对更少。
执行 bgsava 快照时,数据能被批改吗?
那问题来了,执行 bgsava 过程中,因为是交给子过程来构建 RDB 文件,主线程还是能够持续工作的,此时主线程能够批改数据吗?
如果不能够批改数据的话,那这样性能一下就升高了很多。如果能够批改数据,又是如何做到到呢?
间接说论断吧,执行 bgsava 过程中,Redis 仍然 能够持续解决操作命令 的,也就是数据是能被批改的。
那具体如何做到到呢?要害的技术就在于 写时复制技术(Copy-On-Write, COW)。
执行 bgsava 命令的时候,会通过 fork()
创立子过程,此时子过程和父过程是共享同一片内存数据的,因为创立子过程的时候,会复制父过程的页表,然而页表指向的物理内存还是一个。
只有在产生批改内存数据的状况时,物理内存才会被复制一份。
这样的目标是为了缩小创立子过程时的性能损耗,从而放慢创立子过程的速度,毕竟创立子过程的过程中,是会阻塞主线程的。
所以,创立 bgsave 子过程后,因为共享父过程的所有内存数据,于是就能够间接读取主线程里的内存数据,并将数据写入到 RDB 文件。
当主线程对这些共享的内存数据也都是只读操作,那么,主线程和 bgsave 子过程互相不影响。
然而,如果主线程要 批改共享数据里的某一块数据 (比方键值对 A
)时,就会产生写时复制,于是这块数据的 物理内存就会被复制一份(键值对 A'
),而后 主线程在这个数据正本(键值对 A'
)进行批改操作。与此同时,bgsave 子过程能够持续把原来的数据(键值对 A
)写入到 RDB 文件。
就是这样,Redis 应用 bgsave 对以后内存中的所有数据做快照,这个操作是由 bgsave 子过程在后盾实现的,执行时不会阻塞主线程,这就使得主线程同时能够批改数据。
仔细的同学,必定发现了,bgsave 快照过程中,如果主线程批改了共享数据,产生了写时复制后,RDB 快照保留的是本来的内存数据,而主线程刚批改的数据,是被方法在这一时间写入 RDB 文件的,只能交由下一次的 bgsave 快照。
所以 Redis 在应用 bgsave 快照过程中,如果主线程批改了内存数据,不论是否是共享的内存数据,RDB 快照都无奈写入主线程刚批改的数据,因为此时主线程的内存数据和子线程的内存数据曾经拆散了,子线程写入到 RDB 文件的内存数据只能是本来的内存数据。
如果零碎恰好在 RDB 快照文件创建结束后解体了,那么 Redis 将会失落主线程在快照期间批改的数据。
另外,写时复制的时候会呈现这么个极其的状况。
在 Redis 执行 RDB 长久化期间,刚 fork 时,主过程和子过程共享同一物理内存,然而途中主过程解决了写操作,批改了共享内存,于是以后被批改的数据的物理内存就会被复制一份。
那么极其状况下,如果所有的共享内存都被批改,则此时的内存占用是原先的 2 倍。
所以,针对写操作多的场景,咱们要注意下快照过程中内存的变动,避免内存被占满了。
RDB 和 AOF 合体
只管 RDB 比 AOF 的数据恢复速度快,然而快照的频率不好把握:
- 如果频率太低,两次快照间一旦服务器产生宕机,就可能会比拟多的数据失落;
- 如果频率太高,频繁写入磁盘和创立子过程会带来额定的性能开销。
那有没有什么办法不仅有 RDB 复原速度快的长处和,又有 AOF 失落数据少的长处呢?
当然有,那就是将 RDB 和 AOF 合体应用,这个办法是在 Redis 4.0 提出的,该办法叫 混合应用 AOF 日志和内存快照,也叫混合长久化。
如果想要开启混合长久化性能,能够在 Redis 配置文件将上面这个配置项设置成 yes:
aof-use-rdb-preamble yes
混合长久化工作在 AOF 日志重写过程。
当开启了混合长久化时,在 AOF 重写日志时,fork
进去的重写子过程会先将与主线程共享的内存数据以 RDB 形式写入到 AOF 文件,而后主线程解决的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 形式写入到 AOF 文件,写入实现后告诉主过程将新的含有 RDB 格局和 AOF 格局的 AOF 文件替换旧的的 AOF 文件。
也就是说,应用了混合长久化,AOF 文件的 前半部分是 RDB 格局的全量数据,后半局部是 AOF 格局的增量数据。
这样的益处在于,重启 Redis 加载数据的时候,因为前半部分是 RDB 内容,这样 加载的时候速度会很快。
加载完 RDB 的内容后,才会加载后半局部的 AOF 内容,这里的内容是 Redis 后台子过程重写 AOF 期间,主线程解决的操作命令,能够使得 数据更少的失落。
举荐浏览
图解 Reids | AOF 长久化
图解 Reids | 缓存雪崩、击穿、传统
回过头发现,这次的文章图画的好少啊,有失图解工具人名称哈哈。
比拟赶,就没去细想技术图,不过文字描述的还是很顺畅,通俗易懂的,嘻嘻。