Redis 持久化

Redis 如何实现数据不丢失?

Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis重启后,内存中的数据就会丢失,为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到 磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。

Redis中共有三种数据持久化的方式:

  • AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里
  • RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘
  • 混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RDB 的优点

AOF 日志是如何实现的?

Redis 在执行完一条写操作命令后,就会把该命令以追加的方式写入到一个文件里,然后redis 重启时,会读取该文件记录的命令,然后逐一执行命令的方式来进行数据恢复。

AOF 持久化功能的实现可以简单分为五步:

  1. 命令追加:所有的写命令会追加到 AOF 缓冲区中
  2. 文件写入:将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用 write 函数(系统调用),write 将数据写入到了系统内核缓冲区之后直接返回(延迟重写)。此时并没有同步到磁盘
  3. 文件同步:AOF 缓冲区根据对应的持久化方式(fsync策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用),fsync 针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞知道写入磁盘后返回,保证了数据的持久化。
  4. 文件重写:随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的
  5. 重启加载:当 redis 重启时,可以加载 AOF 文件进行数据恢复。

为什么先执行命令,再把数据写入日志呢?

Redis 是先执行写操作命令后,才将该命令记录到 AOF 日志里的,这么做其实有两个好处。

  • 避免额外的检查开销:因为如果先将写操作命令记录到 AOF 日志里,再执行该命令的话,如果当前的命令语法有问题,那么如果不进行命令语法检查,该错误的命令记录到 AOF 日志里后,Redis 在使用日志恢复数据时,就可能会出错。
  • 不会阻塞当前写操作命令的执行:因为当写操作命令执行成功后,才会将命令记录到 AOF 日志。

当然,这样做也会有风险:

  • 数据可能丢失:执行写操作命令和记录日志是两个过程,当Redis 在没来得及将命令写入到硬盘时,服务器宕机了,这个数据就会有丢失的风险。
  • 可能阻塞其他操作:由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前命令的执行,但因为 AOF 日志 也是在主线程中执行,所以当 Redis 把日志文件写入磁盘的时候,还是会阻塞后续的操作无法执行

AOF 写回策略有几种?

Redis 写入 AOF 日志的过程,如下图:

具体过程:

  1. Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区;
  2. 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据(命令 )写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache ,等待内核将数据写入硬盘
  3. 具体内核缓冲区的数据什么时候写入硬盘,由内核自己决定

Redis 提供了 三种 写回硬盘的策略,控制的就是上面说的第三步的过程。在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下三种参数可填:

  • Always :每次写操作命令执行完后,同步 AOF 日志数据写回硬盘
  • Everysec :每次写操作命令完成后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区的内容写回到硬盘
  • No :每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。不由Redis控制写回硬盘的时机。

AOF日志过大会触发什么机制?

AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。如果当 AOF 日志文件过大就会带来性能问题,比如 重启 Redis 后,需要读AOF文件的内容以恢复数据,如果文件过大,整个恢复过程就会很慢。

所以,为了Redis 为了避免 AOF 文件越写越大,提供了 AOF 重写机制,当 AOF 文件的大小超过所设定的阈值后,Redis 会启用 Redis 重写机制,来压缩 AOF 文件。

AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值用一条命令记录到 [新的 AOF 文件],等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF文件。

eg:在没有重写机制前,假设前后执行了 [set name xiaolin] 和 [set name xiaolincoding] 这两个命令的话,就会将这两个命令记录到 AOF 文件。

但是在使用重写机制后,就会读取 name 最新的 value (键值对),然后用一条 [set name xiaolincoding] 命令记录到新的 AOF 文件,之前第一个命令就没必要记录了,因为它属于 [历史] 命令,没有作用了。这样一来,一个键值对在重写日志中只有一条命令就行了。

重写工作完成后,就会将新的 AOF 文件覆盖现有的 AOF 文件,这就相当于压缩了 AOF 文件,使得 AOF 文件体积小了。

重写 AOF 日志的过程是怎样的?

Redis 的重写 AOF 过程是由后台进程 bgrewriteaof 来完成的,这么做由两个好处:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程;
  • 子进程带有主进程的数据副本,这里使用子进程而不是线程,因为如果使用锁来保证数据的安全,这样会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生 [写时复制],于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的AOF文件)。

但是重写过程中,主进程依然可以正常处理命令,那么问题来了,重写 AOF 日志过程中,如果主进程修改了已经存在 key-value ,那么会发生写时复制,此时这个 key-value 数据在子进程的内存数据就跟主进程的内存数据不一致了,这时要怎么办呢?

为了解决这种数据不一致的问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在创建 bgrewriteaof 子进程后就开始使用。

在重写 AOF 期间,当 Redis 执行完一个写命令后,它会同时将这个写命令写入到 [ AOF缓冲区 ] 和 [ AOF重写缓冲区 ]

也就是说,在 bgrewriteaof 子进程执行 AOF 重写期间,主进程需要执行以下三个工作:

  • 执行客户端发来的命令
  • 将执行后的写命令追加到 [AOF 缓冲区]
  • 将执行后的写命令追加到 [AOF 重写缓冲区]

当子进程完成 AOF 重写工作(扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志)后,会向主进程发送一条信号,信号是进程间通讯的一种方式,而且是异步的。

主进程收到该信号后,会调用一个信号处理函数,该函数主要做以下工作:

  • 将 AOF 重写缓冲区中的所有内容追加到新的 AOF 文件中,使得新旧两个 AOF 文件锁保存的数据库状态一致
  • 新的AOF 的文件进行改名,覆盖现有的 AOF 文件

信号函数执行完后,主进程就可以继续像往常一样处理命令了。


RDB 快照是如何实现的?

因为 AOF 日志记录的是操作命令,不是实际数据,所以用 AOF 方法做故障恢复时,需要全量把日志都执行一遍,一旦 AOF 日志非常多,势必会造成 Redis 的恢复操作缓慢。

为了解决这个问题,Redis 增加了 RDB 快照。所谓的快照,就是记录某一瞬间的东西(与MySQL中的 Read View 类似)。

所以,RDB快照就是记录某一个瞬间的内存数据,记录的是实际数据,而 AOF 文件记录的是命令操作的日志,而不是实际数据。

因此在 Redis 恢复数据时,RDB 恢复数据的效率比 AOF 高一些,因为直接将 RDB 文件读入内存就行了,不需要像 AOF 那样还需要额外执行操作命令的步骤才能恢复数据。

RDB 做快照时会阻塞线程吗?

Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave ,他们的区别就是在于是否在 [主线程] 里执行:

  • 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程
  • 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞

Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,默认会提供以下配置:

save 900 1
save 300 10
save 60 10000

选项名虽然是 save ,但实际上执行的时 bgsave 命令,也就是会创建子进程来生成 RDB 快照文件。只要满足上面条件的任意一个,就会执行 bgsave ,它们的意思分别是:

  • 900秒内,对数据库进行了至少一次修改
  • 300秒内,对数据库进行了至少 10次修改
  • 60秒内,对数据库进行了至少 10000 次修改

PS:Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的 [所有数据] 都记录到磁盘中。所以执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。

RDB 在执行快照的时候,数据能修改吗?

可以,在执行 bgsave 的过程中,Redis 依然可以继续处理操作命令,也就是数据是能被修改的,关键的技术就在于写时复制技术

执行 bgsave 命令的时候,会通过 fork() 创建子进程,此时子进程和父进程时是共享一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个,此时如果主线程执行读操作,则主线程和 bgsave 子进程互相不影响。

如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子进程会把该副本数据写入 RDB 文件,在这个过程中,主线程仍然可以直接修改原来的数据。


为什么会有混合持久化?

RDB 优点是数据恢复速度快,但是快照的频率不好把握。频率太低,丢失的数据就会比较多,频率太高就会影响性能。

AOF 优点是丢失数据少,但是数据恢复不快。

为了集成两者的优点, Redis 4.0 提出了混合使用 AOF 日志和内存快照,也叫混合持久化,即保证了 Redis 重启速度,又降低了数据丢失的风险。

混合持久化工作在 AOF 日志重写过程,当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的 AOF 文件。

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据

这样的好处在于,重启 Redis 加载数据的时候,由于 前半部分是 RDB 内容,这样加载的时候速度就会很快

加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失

混合持久化优点

  • 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,降低了大量数据丢失的风险

混合持久化缺点

  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差
  • 兼容性差,如果开启了混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。

 如何选择 AOF 或这 RDB?

RDB 比 AOF 优秀的地方:

  • RDB 文件存储的内容是经过压缩的二进制数据,保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难修复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得很大的时候,Redis 能够在后台自动重写 AOF 。新的 AOF 文件和原有的AOF 文件所保存的数据库状态一样,但体积更小。
  • 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要一次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候 RDB 速度更快

AOF 比 RDB 优秀的地方:

  • RDB 的数据安全性不如 AOF ,不能实现实时或者秒级持久化数据。生成 RDB 文件的过程是比较繁重的,虽然 bgsave 子进程写入 RDB 文件不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务器干宕机。 AOF 支持秒级数据丢失,只需要追加命令到 AOF 文件,操作轻量
  • RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB ,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
  • AOF 以一种易于理解和解析的格式包含所有操作的日志。可以轻松地导出 AOF 文件进行分析,也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。

综上

  • Redis 保存的数据丢失一些也没有什么问题的话,可以选择使用 RDB。
  • 不建议单独使用 AOF ,因为时不时建立一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误
  • 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化 或者 开启 RDB 和 AOF 混合持久化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值