菜鸟笔记
提升您的技术认知

彻底搞懂redis的持久化-ag真人游戏

很简单,因为 redis 是基于内存的。数据如果不进行持久化,当服务器重启或者宕机的时候数据是无法恢复的,所以为了保证数据的安全性,我们需要将内存中的数据持久化到磁盘中。

redis 提供了两种持久化的方式,分别是 rdb 和 aof。

  • rdb : redis的默认持久化方式,是基于 快照 来实现的,当符合一定条件的时候 redis 会自动将内存中的数据进行快照然后持久化到磁盘中。
  • aof : redis默认没有开启 aof 持久化,需要在配置文件中设置 appendonly true 开启。它存储的是 redis 的 顺序指令序列 。

save 阻塞方式

在 redis 中有一个命令可以触发 rdb 持久化,但是这个操作会阻塞 redis。

这个命令是 save,我们都知道 redis 是单线程的,如果持久化进行 特殊的处理 的话,那么就会阻塞其他命令导致 redis 短时间内不可用,如果 rdb 文件很大,那么刷盘操作将会数十秒,严重影响可用性,所以我们一般都不会使用 save 命令。

bgsave 后台方式

bgsave 顾名思义,就是 后台进行保存 。当执行这条命令的时候 redis 就会进行一些 特殊处理 。

什么特殊处理呢?

首先 redis 的主进程会调用 glibc 的函数 fork 产生一个子进程,此时会将文件 持久化全部交给子进程去处理,那么这时父进程就可以继续处理用户的请求了(bgsave执行之后直接会返回)。当然,在主进程进行 fork 操作的时候可能也会对用户请求命令 产生短暂的阻塞 。

凡事有利必有弊,你看 bgsave 这么好,难道没有缺点么? 答案是有的,在哪呢?我们先来了解一下 cow 机制吧。

cow

cow (copy on write),也就是 写时复制 。我们知道 rdb 持久化需要遍历内存中的数据,就像下面那张图一样。

因为我们需要的是在子进程产生的那一瞬间的数据(快照),如果此时因为用户请求在主进程修改了内存中的数据,那么子进程遍历的内存就会被更改,这个时候就不是快照数据了。

所以这里就使用了产生快照的一种机制—— cow 。我们知道上面的数据段其实由很多操作系统的组合而成的。cow 其实就是在主进程需要修改内存中的数据的时候,首先将需要修改的数据所在的页进行复制,然后再复制的页面上进行修改。当进行页的复制的时候就会占用额外的内存,这也是 bgsave 占用内存比 save 多的原因,但是不用过分担心,因为再保存期间不会出现大量的用户请求来修改数据,额外使用的内存也不会很多。

自动触发rdb

在 redis 中会有几种情况下进行 rdb 的持久化,所以即使你在配置文件中关闭了 rdb ,redis 还是会进行 rdb 的持久化。

  • 满足条件时 在 redis 的配置文件中有这么三条配置。
save 900 1
save 300 10 
save 60 10000 

这其实就是 rdb 中默认开启的原因,它的格式是这样的 save seconds changetimes,save 后面的第一个数字是时间,第二个数字是修改次数,这三条配置的意思就是 在900秒内进行了1次修改或者在300秒内进行了10次修改或者在60秒内进行了10000次修改 会进行 rdb 的自动持久化。

  • shutdown 当 redis 正常关闭的时候会进行 rdb 持久化。
  • flushall 会产生一个空的 rdb 文件
  • 主从复制进行全量复制的时候(目前做了解就行,我在后面的文章会讲到 redis 集群)

rdb的优点

  • rdb 文件,其中做了些压缩,存储的是数据,可以快速的进行灾难恢复
  • 适合做冷备。

rdb的缺点

  • 容易丢失数据,因为 rdb 需要遍历整个内存中的所有数据,所以进行一次 rdb 操作是一个费事费力的操作,为了保证 redis 的高性能,你需要尽量减少 rdb 的持久化,所以你可能会丢失一段时间的数据。

默认情况下 redis 是没有开启 aof 持久化的,你需要在配置文件中进行相应的配置。

appendonly yes # 默认为no这里设置yes开启
dir ./ # aof文件目录
appendfilename "appendonly-6379.aof" #aof文件名

开启 aof 持久化之后,redis 会根据 aof 持久化策略 来进行相应的持久化操作,具体配置是在 appendfsync 配置。

# appendfsync always 每次进行修改操作就进行写盘
appendfsync everysec 每秒进行一些写盘
# appendfsync no 从不

redis一共提供了三个参数,一般考虑设置 everysec 每秒写盘,这样既能少影响效率也能减少数据丢失量。

aof原理

aof 日志中存放的是对于 redis 的操作指令,有了 aof 日志我们就可以使用它进行对 redis 的重放。

比如此时我们 aof 日志中记录了 set hello world 和 sadd userset fancisq 这两条命令,我们就可以对一个空的 redis 实例进行此 aof 文件的重放,最终这个空的 redis 就有了上面两条记录。

你可能会发现 redis 中的这两个持久化方式很像 mysql 中的 bin log 和 redo log,但是你需要注意的是 redis 中的 aof 是先执行命令再存日志的。这和 mysql 中的 wal 机制截然相反。

为什么呢? 我觉得有两点。

  1. redis 是弱事务的,我们不需要保证数据的强一致。在 mysql 中我们使用了 redo log 两阶段提交 来保证了 save-crash 能力,而在 redis 中我们显然不需要这么做,假设这条命令执行完之后还没来得及写日志就宕机了,那就没了,因为弱事务,我们大可不必保证数据必须存在。
  2. 为了避免错误指令的日志存储,如果先写日志也就意味着我们一开始没有做相应的 逻辑处理和参数校验 ,所以这样会 先记录到很多错误指令 ,但是我们知道 aof 文件是需要 瘦身 的,这些错误指令会给 aof 瘦身带来很多麻烦。

aof重写

上面提到的 瘦身 其实就是 aof重写 ,我们知道 aof 文件中存储的是指令顺序,当 redis 长时间运行时会产生很多指令。

比如 set a b,set a c,set a d.....

其实上面三条就是对 key 为 a 的数据进行操作了,在 rdb 中它可能只存了 a = d ,但是因为 aof 的指令机制,它必须存在三条,但前面的是无意义的,这样会浪费很多空间并且给 aof 重放带来麻烦。

所以 redis 会在 aof文件过大(符合某种条件)的时候进行自动的 aof 重写。对应的在配置文件中有这样两条配置。

# 下面两条需要同时满足
# 表示当前 aof 文件超过上次 aof文件大小的百分之多少时会进行重写,如果没有重写过则以启动时的大小为标准
auto-aof-rewrite-percentage 100 
# 文件大于多少的时候进行重写
auto-aof-rewrite-min-size 64mb

那么,aof 是如何重写的呢?

bgrewriteaof

这是一条 aof 重写命令(上述的重写过程其实就是 bgrewriteaof),和 bgsave 一样,redis 也是会 fork 一个子进程,让子进程去负责 aof 文件的重写。大致流程如下:

aof的优缺点

  • 缺点: 在同等数据量的情况下,aof 文件的大小要比 rdb 文件大得多,如果使用它进行内存状态恢复需要花费很长时间
  • 优点: 持久化快,能减少数据丢失的量,在配置 everysec 的情况下最多只会丢失秒级的数据。

在 redis 4.0之前,我们一般会开启 aof 然后再需要恢复内存状态的时候弃用 aof日志重放 ( 如果使用rdb的话会丢失大量数据 )。但如果 redis 实例很大,aof 文件也很大的时候会导致 redis 重启非常慢。

为了解决这个问题,在 redis 4.0之后我们可以将 rdb文件和 aof增量日志存储在一起。如果这个时候我们进行内存状态恢复可以先使用前面的 rdb 部分,然后再使用 rdb 持久化之后产生的增量aof日志 来进行内存状态恢复以减少时间。

  • 一般来说如果对数据的安全性还是有一定要求的话,应该同时使用两种持久化功能
  • 如果可以承受分钟级别的数据丢失可以仅仅使用 rdb。
  • aof 尽量使用 everysec 配置,既能保证数据安全又能保证性能效率。
  • rdb 下关闭 save seconds changetimes 这个自动持久化机制,或者合理使用参数。

分享思维导图

网站地图