Redis持久化

为了防止数据丢失以及服务重启时能够恢复数据,Redis支持数据的持久化(和Memcached的主要区别之一),主要分为两种方式,分别是RDB和AOF。当然实际场景下还会使用这两种的混合模式。

RDB快照持久化

触发方式

rdb触发方式分为手动触发和自动触发两种:

  • 手动触发:redis客户端执行save或bgsave命令

    • save命令: 阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用 。

    • bgsave命令: Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短 ,其流程图如下:

  • 自动触发:在一下4中情况下会自动触发

    • redis.conf中配置save m n,即在m秒内有n次修改时,自动触发bgsave生成rdb文件;
    • 主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点;
    • 执行debug reload命令重新加载redis时也会触发bgsave操作;
    • 默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作。

相关配置

  • 持久化周期配置

    1
    2
    3
    4
    5
    6
    7
    8
    # 周期性执行条件的设置格式为 
    save <seconds> <changes>
    # 默认的设置为:
    save 900 1 #如果900秒内有1条Key信息发生变化,则进行快照
    save 300 10 #如果300秒内有10条Key信息发生变化,则进行快照
    save 60 10000 # 如果60秒内有10000条Key信息发生变化,则进行快照
    # 以下设置方式为关闭RDB快照功能
    save ""
  • 其他相关配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 文件名称
    dbfilename dump.rdb
    # 文件保存路径,默认"./",即Redis服务的主目录
    dir /home/work/app/redis/data/
    # 如果持久化出错,主进程是否停止写入
    stop-writes-on-bgsave-error yes
    # 是否压缩
    rdbcompression yes
    # 导入时是否检查。一个64位的CRC冗余校验编码会被放置在RDB文件的末尾,以便对整个RDB文件的完整性进行验证。这个功能大概会多损失10%左右的性能,但获得了更高的数据可靠性。所以如果您的Redis服务需要追求极致的性能,就可以将这个选项设置为no
    rdbchecksum yes

优缺点

  • 优点
    • RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
    • Redis加载RDB文件恢复数据要远远快于AOF方式.
  • 缺点
    • RDB方式实时性不够,无法做到秒级的持久化;
    • 每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;
    • RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全;
    • 版本兼容RDB文件问题.

深入理解

  • Redis开辟的内存空间通常较大,在将内存数据同步到硬盘可能会持续比较长的时间。在持久化这段时间一般都会收到数据写操作请求,如何保证数据一致性?

    RDB的核心思路就是Copy-on-Write, 需要压缩写入磁盘上的数据在内存中不会发生变化 。在持久化这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。

  • 在进行快照操作的这段时间,如果发生服务崩溃怎么办?

    崩溃了就弃用本次持久化。持久化过程会创建一个临时文件,只有持久化完整过程结束后,才会用该临时文件替换上一次的备份。

  • 可以每秒做一次快照吗?

    快照间隔时间越短,服务崩溃时丢失的数据越少,但是频繁的快照会带来两方面问题:

    • 磁盘压力过大: 多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
    • 阻塞主线程:虽然bgsave会fork子线程执行IO操作,但是fork子线程会阻塞主线程。 如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了。

AOF追加文件持久化

和RDB相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启: appendonly yes

实现方法

开启AOF后,AOF日志记录Redis的每个写命令,分3步:命令追加、文件写入和文件同步。

  • 命令追加: 服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区 。

  • 文件写入和同步: 关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:

    名称 写会时机 优点 缺点
    Always 同步写回 可靠性高,数据基本不丢失 开销过大,影响性能
    Everysec 每秒写会 性能适中 宕机时丢失1秒数据
    No 操作系统控制写回 性能好 宕机时丢失数据较多

配置文件

名称 说明
appendonly yes/no,是否开启AOF,默认是关闭的
appendfilename AOF持久化文件名
dir AOF文件保存路径
appendfsync 同步策略,always/everysec/no
no-appendfsync-on-rewrite 设置no表示在aof重写期间不会将这段时间内发生的写命令放入操作系统
auto-aof-rewrite-percentage 生产环境不可能手动进行AOF持久化,因此需要配置何时自动触发AOF。该配置项表示当前AOF文件大小超过上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件
auto-aof-rewrite-min-size 和auto-aof-rewrite-percentage类似,不过这里给的不是百分比,而是具体文件大小。比如设置为64mb,表示当AOF文件大小超过64mb后,重新开始新的AOF文件

AOF重写

Aof重写过程如下图所示,主线程首先fork出子进程用于重写aof日志,子进程在重写日志完成后,通知主进程追加aof日志缓冲,最后用新日志文件替换掉原来的日志文件。

下面总结了AOF重写的常见问题:
  • 为什么要进行AOF重写?

    AOF会记录每个写命令到AOF文件,随着时间越来越长,AOF文件会变得越来越大。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响,而且AOF文件越大,数据恢复也越慢。

  • AOF重写会阻塞吗?

    和RDB一样,AOF重写也会fork一个子进程,重写过程不阻塞,但是fork子进程会阻塞主进程。

  • AOF何时会重写?

    如前文“配置项”所述,Redis根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 两个配置项决定何时重写。

  • AOF重写时,有新数据写入怎么处理?

    如前文图片所示,Redis会将新数据放到AOF重写缓冲区,子进程重写完毕后,将缓冲区内容写入新日志。

    注:在高并发系统中,重写过程可能有大量新入局进入,导致缓冲区积累数据过多。Redis后来通过Linux管道技术,让aof重写期间能同时进行回放,这样aof重写结束后,缓冲区不会积累过多数据。

  • 主线程fork出子进程的是如何复制内存数据的?

    fork子进程时,为了避免一次性拷贝过多数据造成阻塞。fork子进程时,子进程会拷贝父进程的页面,而不会拷贝物理内存。这时候两个进程使用相同的内存地址空间。

    此时主进程是可以有数据写入的,如下图所示:

在主进程有数据写入时,如果这个数据恰好在页C中,主进程会创建一个页C的副本,将其映射到主进程中,而子进程还是使用原来的页C。

  • AOF重写过程中哪些时候会阻塞?

    • fork子进程时,需要拷贝虚拟页表,会对主进程阻塞。
    • 主进程有bigkey写入时, 操作系统会创建页面的副本,并拷贝原有的数据,会对主线程阻塞。
    • 子进程重写日志完成后,主进程追加aof重写缓冲区时可能会对主线程阻塞。
  • 为什么AOF重写不复用原AOF日志

    • 父子进程写同一个文件会产生竞争问题,影响父进程的性能。
    • 如果AOF重写过程中失败了,相当于污染了原本的AOF文件,无法做恢复数据使用。

RDB和AOF混合方式(4.0版本)

该模式下,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作,如下图所示:

## 从持久化中恢复数据

从持久化文件中恢复数据,只需要重新启动Redis即可。其流程如下图所示:

那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。

参考资料

  1. https://pdai.tech/md/db/nosql-redis/db-redis-x-rdb-aof.html
  2. https://javaguide.cn/database/redis/redis-questions-01
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信