Redis持久化机制--RDB和AOF

redis持久化方式:AOF和RDB持久化过程,AOF和RDB文件配置,各个配置项解读,AOF文件格式,AOF的rewrite压缩机制,RDB和AOF混合使用

Redis 是一个内存数据库 ,所以其运行效率非常高。但也存在一个问题:内存中的数据 是不持久的,若主机宕机或 Redis 关机重启,则内存中的数据全部丢失。因此 Redis 具有持久化功能Redis 通过数据快照或追加操作日志的形式将数据持久化到磁盘。 根据持久化使用技术的不同,Redis 的持久化分为两种:RDB 与 AOF。

当系统重新启动时,会自动加载持久化文件,并根据文件中数据库状态描述信息将数据恢复到内存中。

RDB

  • RDB(Redis DataBase)是 Redis默认开启 的持久化⽅式,是指将内存中某一时刻的数据快照全量写入到指定的 rdb 文件中 。当 Redis 启动时会自动读取 rdb 快照文件,将数据从硬盘载入到内存,以恢复 Redis 关机前的数据库状态。
  • 由于 RDB 是生成的数据快照,因此⽣成的 RDB ⽂件⾮常适合⽤于全量复制、数据备份等场景
  • RDB 创建数据快照的时间间隔可以通过 redis.conf 配置文件中的"save"配置选项进⾏设置或者通过命令即时创建快照,如"save 900 1"表示如果 900 秒内有⾄少 1 个 key 变化,则创建⼀个 snapshots 快照。

RDB 持久化命令

save

执行 save 命令可立即进行一次持久化保存。save 命令是同步保存操作,由 Redis 主进程执行,因此执行期间会**阻塞主进程**,Redis 不能处理任何读写请求。

bgsave

执行 bgsave 命令可立即进行一次持久化保存,主进程会 fork 出一个子进程 ,由该子进程负责完成保存过程。在子进程进行保存过程中,不会阻塞 redis-server 进程对客户端读写请求的处理

fork() 函数是用于在操作系统中创建新进程的系统调用。它会将当前进程的内存内容完整地复制到内存的另一个区域,从而创建一个新的子进程。

RDB 持久化过程

在进行持久化过程中,如果主进程接收到了用户写请求,则系统会将内存中发生数据修改的物理块 copy 出一个副本。等内存中的全量数据 copy 结束后,会再将 副本中的数据 copy 到 RDB 临时文件。这个副本的生成是由于**Linux 系统的写时复制技术 (Copy-On-Write)**实现的。

AOF

AOF 持久性记录服务器接收到的每个写操作。然后在服务器启动时再次重复执行这些操作,从⽽重建原始数据集。

AOF 持久化过程

  1. 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
  2. 文件写入(write) :将 AOF 缓冲区的数据写入到 AOF 文件过程中要调用write函数(系统调用),write先把数据写到系统内核缓冲区后直接返回,此时还没写到磁盘 AOF 文件中(延迟写)。
  3. 文件同步(fsync) :AOF 缓冲区根据对应的持久化方式( fsync 策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用)进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
  4. 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  5. 如果在 rewrite 过程中又有写操作命令追加,那么这些数据会暂时写入 aof_rewrite_buf 缓冲区。等将全部 rewrite 计算结果写入临时文件后,会先将AOF 重写缓冲区 中 的数据追加 到临时文件,然后再 rename 为磁盘文件的原名称,覆盖原文件。 > write:写入系统内核缓冲区之后直接返回(仅仅是写到缓冲区),不会立即同步到硬盘。虽然提高了效率,但也带来了数据丢失的风险。同步硬盘操作通常依赖于系统调度机制,Linux 内核通常为 30s 同步一次,具体值取决于写出的数据量和 I/O 缓冲区的状态。
    fsync:强制刷新系统内核缓冲区(同步到磁盘),确保写磁盘操作结束才会返回。

AOF 持久化 fsync 策略

  • appendfsync always:主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。
  • appendfsync everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数 (系统调用)同步一次 AOF 文件(write+fsyncfsync间隔为 1 秒)
  • appendfsync no:主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsyncfsync 的时机由操作系统决定)。

为了兼顾数据和写入性能,可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件。

AOF 文件拆分

从 Redis7.0 发布看 Redis 的过去与未来

从 Redis 7.0.0 开始,Redis 使用了 Multi Part AOF 机制。顾名思义,Multi Part AOF 就是将原来的单个 AOF 文件拆分成多个 AOF 文件。在 Multi Part AOF 中,AOF 文件被分为三种类型,分别为:

  • BASE:表示基础 AOF 文件,它一般由子进程通过重写产生,该文件最多只有一个。
  • INCR:表示增量 AOF 文件,它一般会在 AOFRW 开始执行时被创建,该文件可能存在多个。
  • HISTORY:表示历史 AOF 文件,它由 BASE 和 INCR AOF 变化而来,每次 AOFRW 成功完成时,本次 AOFRW 之前对应的 BASE 和 INCR AOF 都将变为 HISTORY,HISTORY 类型的 AOF 会被 Redis 自动删除。

为什么 AOF 是执行完命令才记录日志

关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。

  • 避免额外的检查开销:AOF 记录日志不会对命令进行语法检查,如果先写入日志结果命令出错,则还得修改日志;
  • **不会阻塞当前写操作命令的执行:**先写日志然后执行命令,结果命令出错,还得回去改日志,就阻塞了当前命令,甚至影响下一个命令。

AOF 重写

什么是重写?

随着执行的写命令越来越多,AOF 文件也越来越大,为解决这一问题,要对 AOF 压缩,即重写。假如先后执行了 set num:1 和 set:num:2,实际结果是 2,那么重写后就不需要 set num:1 这个记录了。

重写过程中会把新数据记录到新的 aof 文件,防止对原先的 aof 文件造成污染。

  • redis 的重写 AOF 过程是通过 fork 系统调用生成后台子进程 bgrewriteaof 来完成的**,** 如果在 rewrite 过程中又有写操作命令追加,那么这些数据会暂时写入 aof_rewrite_buf 缓冲区。等将全部 rewrite 计算结果写入临时文件后,会先将AOF 重写缓冲区 中 的数据追加到临时文件,然后再 rename 为磁盘文件的原名称,覆盖原文件。

  • 子进程带有主进程的数据副本,这里使用子进程而不是线程,因为如果是使用线程,多线程共享内存 ,那么在修改共享内存数据的时候,需要通过加锁 来保证数据的安全,而这样就会降低性能 。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读 的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」(这里的写时复制和 RDB 的写时复制是同一个),于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

  • 主进程在通过 fork 系统调用生成 bgrewriteaof 子进程时,操作系统会把主进程的「页表」复制一份给子进程 ,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。这样父子进程就可以共享只读数据了。

    当父进程或者子进程在向这个内存发起写操作时,由于违反权限 CPU 会触发写保护中断,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系 ,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这个过程被称为写时复制

混合持久化

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计