四、redis持久化

Redis 支持RDB和AOF两种持久化机制,之所以存在两种,是因为它们各自适用于不同的场景、不同的阶段。我接下来对其进行介绍,并分析我们在实际场景中如何使用配置。

RDB

有两个重要命令,save和bgsave,一个是同步,一个是异步,完成后会在指定的目录生成一份压缩文件(默认是dump.rdb,使用LZF算法,可以通过dbfilename配置,dir配置文件夹)。

  • save: 阻塞当前Redis服务器、直到RDB过程完成,因为会阻塞线程,所以基本被废弃了。
  • bgsave: Redis主线程会进行fork操作,创建子进程,RDB持久化过程由子进程负责,虽然也会有阻塞,但只阻塞fork阶段,这个命令是对save的一个改进
  • 自动保存,在程序中以下情景会自动触发bgsave
    1. 使用save相关配置,如"save m n",表示m秒内数据存在n次修改时自动触发,可以配置多个规则
    2. 如果从节点执行全量复制,那么master节点会自动执行bgsave,然后将RDB文件发给从节点
    3. 执行debug reload 命令重新加载Redis时,也会触发
    4. 在shutdown命令执行后,如果没有开启AOF持久化功能,会触发bgsave

优势:

  • 是一个压缩后的二进制文件,代表某个时间点的快照,特别适合发送到其他节点。
  • Redis加载RDB的速度远远快于AOF

缺陷

  • 没办法实时的持久化,bgsave是一个重量级的操作,不可能频繁执行
  • RDB文件使用特定格式保存

AOF

为了应对RDB的缺点,AOF方式产生。以独立日志的方式记录每次命令,重启的时候再重新执行AOF文件中的命令达到恢复数据的目的。

  • 开启
    默认是不开启的,需要配置 appendonly yes,默认文件名是appendonly.aof,路径与RDB一致。

  • 工作流程

    1. 将写入命令追加到aof_buf缓冲区中
    2. AOF缓冲区根据对应的策略向硬盘做同步工作fsync
    3. 随着AOF文件越来越大,需要定期重写,达到压缩的目的
    4. 当redis重启时,可以加载AOF文件进行数据恢复
  • 文件格式
    为文本协议格式,也就是之前讲过的通信协议的格式,如:·*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
    之所以采用文本协议,是因为其兼容性好,而且可读性比较强,可以直接修改

  • 文件同步: appendfsync配置

    1. always 写入aof_buf后立即同步到文件,fsync完成后线程返回
    2. everysec 建议的配置,命令写入aof_buf后调用系统write操作,完成后线程返回。fsync同步文件操作,由另一个线程每秒调用一次。
    3. no 写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步的步骤由系统来做,最长30s
    • write:利用页缓冲区来提高硬盘IO性能,会有短暂的延迟,如果真正同步之前发生宕机,则会丢失数据。因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性
    • fsync:针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync将阻塞直到写入硬盘后返回。
  • 重写机制,重写采用了以下方法实现了压缩

    1. 进程内超时的数据不再写入
    2. 无效命令不再写入(也就是中间过程)
    3. 合并多条命令,比如lpush list a,lpush list b -> lpush list a b c.
    • 手动触发: bgrewriteaof
    • 自动触发: auto-aof-rewrite-min-size和auto-aof-rewrite-percentage
    • 重写过程: 主进程fork一个新进程,并使用AOF重写缓冲区保存重写期间的数据。重写完成后,父进程吧重写缓冲区的数据刷新到新的文件。

重启加载

记住,优先加载AOF

重启加载

运维优化

  1. fork操作
    fork操作是一个重量级操作,子进程会复制父进程的空间内存页表,在搞ops情况下有可能阻塞大量的命令。

    • 优先使用物理机,或者高效的虚拟化技术
    • 控制Redis实例最大可用内存,耗时时间跟内存(内存页)成正比
    • 合理配置Linux内存分配
    • 降低fork的频率,也就是放款AOF自动触发的机制
  2. 子进程

    • cpu 在子进程将进程中的文件分批写入的时候消耗比较大
    • Redis是密集型服务,所以不要做单核绑定(子进程会抢占),可以使用多实例部署的方式
    • 内存,子进程共享父进程的内存快照,在新命令时,父进程会创建内存页副本,这一部分是增加的内存(AOF还有重写缓冲区)。
    • 因此对于内存,尽量保证只有一个子进程,另外不要再大量写入时触发重写操作。
  3. AOF阻塞
    在子线程同步磁盘时,主线程会不断对比上次AOF同步时间,如果超过2S(everysec方式),则会阻塞等待。如果发生阻塞,很可能是磁盘IO存在高负荷,导致同步变慢。