azwcl
azwcl
Published on 2025-02-08 / 27 Visits
0
0

Redis 持久化

Redis 持久化

1. Redis 持久化

1.1. 提供持久化的方案

1689479335241.jpg

  • RDB (Redis Database):以指定时间间隔执行数据集的时间点快照;
  • AOF (Append Only File):持久性记录服务器接收到的每个写操作,在服务器启动的时候再次重新执行这些操作,从而达到重建原始数据集;
  • 无持久化:完全禁用 Redis 的持久性;
  • RDB + AOF:组合使用 RDB 和 AOF;

2. RDB 快照持久化

2.1. 简介

  1. 是什么?

    • 类似与照片记录一样的效果,将某一时刻的数据和状态以文件的形式写在磁盘之上,即快照;
    • 一旦宕机,快照文件不会丢失,数据的可靠性也得到保证,这个文件称为 RDB 文件;
    • 默认开启;
  2. 干什么?

    • 指定时间间隔内,将内存的数据集快照写入磁盘,即 SHAPSHOT 内存快照,它回复的时候再将硬盘的快照文件直接读回内存中;
    • 保存的文件为:dump.rdb 文件;
  3. 配置项

    • 采用 redis.confSNAPSHOTTINGsave 参数,来进行触发 RDB 持久化条件;
    • save m n :6.0.16 以下配置,在 m 秒内数据集存在 n 次修改,自动触发 bgsave
    • save m1 n1 m2 n2... :6.2 及 7 配置,与 6 无太大变化,就是写到了一行;
    • dir 配置:修改 dump 文件保存路径;默认是 ./
    • dbfilename 配置:修改 dump 文件名;

2.2. 命令触发

2.2.1. save

  • 主程序执行,阻塞当前 redis 服务器;

2.2.2. bgsave

  • 异步后台执行快照;
  • 该触发方式会 fork 一个子进程,由子进程进行复制持久化过程;

linux fork:就是复制当前进程,在内核进程表中创建一个新的进程表象;该进程称之为子进程,被复制的称之为父进程;子进程与父进程完全相同,同时也会复制父进程的数据(堆数据,栈数据和静态数据);

2.3. 优劣势

2.3.1. 优势

  • 非常紧凑的单文件时间点表示;非常适合备份;
  • 非常适合灾难恢复,是一个可以传输到远程数据中心的压缩文件;
  • 最大限度提高了 redis 的性能,因为父进程只做一个操作,即 fork 一个子进程;
  • AOF 相比,RDB 加载速度快;

2.3.2. 劣势

  • 一定时间间隔做备份,如果意外宕机等,则会丢失当前到最后一次快照时间的数据;
  • 内存数据的全量同步,如果数据量过大,会导致 I/O 严重,影响服务器性能;
  • RDB 依赖与主进程 fork,更大的数据集中,可能会导致服务请求的瞬间延迟,可能会几毫秒甚至于一秒钟;fork 操作可能导致短暂的 CPU 资源占用,特别是大数据集的情况下,引起主进程的调度延迟;同时 fork 通过写时复制 (COW) 操作实现共享内存,引起系统级别开销,尤其是数据量大的情况;

2.4. 触发 RDB 的情况

  • 配置文件默认的快照配置;
  • 手动触发命令;
  • 执行 flushallflushdb 命令;
  • 执行 shutdown 命令且没有开启 AOF 持久化命令;
  • 主从复制的时候,主节点自动触发;

2.5. RDB COW

  在 bgsave 的时候,redis 仍然可以继续处理操作命令,数据仍然可以进行修改,此处用到一项关键技术,即:COW ( Copy-On-Write ),写时复制;

  在执行 bgsave 命令的时候,主进程通过 fork 创建一个子进程,子进程此时与父进程使用同一块内存数据(创建子进程的时候,会进行复制父进程页表,但是页表指向的物理内存仍然是同一个);当父进程发生修改的时候,物理内存才会被复制一份;

  对于主进程如果说需要修改共享数据的某一块数据,就会发生写时复制,于是**这块数据物理内存就会被复制一份,主进程在这个数据副本进行修改,而子进程继续将原来数据写入 RDB 文件之中;**这个时候会有一个问题,即主进程修改了共享数据,但是子进程保存的是原本的内存数据,主进程刚修改的数据没法第一时间写入 RDB 文件,只能交给下一次快照了;

  极端情况下,如果所有的共享内存都被修改,则此时内存占用将膨胀到原先 2 倍;

2.5. 其余小知识

  1. 知道最后一次执行快照的时间戳:lastsave
  2. 可以通过 redis-check-rdb 检查修复 dump.rdb 文件;
  3. 禁止快照方式:动态禁止 redis-cli config set save "" ;或者直接快照配置 save 参数;
  4. 配置详解
    • save <seconds> <changes> :自动触发条件;
    • dbfilename :RDB 文件名;
    • dir :存储目录;
    • stop-writes-on-bgsave-error :默认为 yes ,如果配置成 no:表示不在乎数据不一致,或者有其他的手段发现和控制这种不一致,那么在快照写入失败的时候,也能 确保 redis 继续接受新的写请求;即当 bgsave 出错的时候,是否停止 redis 的写请求;
    • rdbcompression :默认为 yes ;对于存储到磁盘的快照,是否使用 LZF 算法进行压缩;
    • rdbchecksum :默认为 yes ;存储快照后,可以让 redis 使用 CRC64 算法进行数据校验,会增加 10% 的性能消耗;
    • rdb-del-sync-files :主从全量同步时,通过 RDB 文件传输实现;如果没有开启持久化,通过不完成后,是否要移除主从同步的 RDB 文件,默认是 no ;如果设计到某些因素,可以设置为 yes ;(只有当 AOF 和 RDB 都关闭的情况下,该配置才会生效;)

3. AOF 持久化

3.1. 简介

  • 以日志的形式来记录每个写操作;只需追加;启动之初会读取文件重新构建数据;
  • 默认情况,redis 不开启 AOF

3.2. 详细介绍

3.2.1. 工作流程

1691019710540.jpg

  1. 客户端会传来源源不断的请求命令;
  2. 命令到达服务端,并非先写入 AOF 文件之中,而是先写入 AOF 缓存中进行保存,当这些命令到达一定量,再写入磁盘,避免频繁 I/O;
  3. AOF 缓冲会根据 三种写回策略 将命令写入磁盘;
  4. 随着写入 AOF 内容增加,避免文件膨胀,会根据规则进行命令合并,又称为 AOF 重写,从而达到压缩文件的目的;
  5. 当服务器重启的时候,会从 AOF 文件中载入数据;

3.2.2. 三种写回策略

配置项 写回时机 优点 缺点
Always 同步写回 可靠性高,数据基本不丢失 性能影响大
Everysec 每秒写回 性能适中 宕机时丢失 1 秒内数据
No 操作系统控制 性能好 宕机时丢失数据多
  • 默认为:Everysec

3.2.3. 如何使用

  1. 开启

    • appendonly 配置项开启,默认为 no ,设置为 yes 即开启;
    • 写回策略配置:appendfsync
    • 保存路径:redis6 时,AOF 和 RDB 一样,通过 dir 参数配置;redis7 时,最终路径由:dir + appenddirname ;
  2. 保存名称

    • redis6 只有一个,即 appendfilename 参数;

    • redis7 分为 multi part aof 设计;拆分为三个文件;

      • base:基本文件,一般由子进程通过重写产生,最多只有一个;
      • incr:增量文件,会在 aofrw 开始执行的时候被创建,可能存在多个;
      • manifest:清单文件,为了管理,引入一个清单文件进行管理,同时为了方便 AOF 备份和拷贝,我们将所有 AOF 文件和清单文件放入一个文件目录,即 appenddirname 配置;
    • 实例:

      1691022433171.jpg

  3. 异常恢复

    • 可以使用:redis-check-aof --fix 来进行修复;

      tip : 如果你不小心 flushall了;然后你有 AOF,其实你可以打开其 incr 文件,然后将最后一行 flushall 删除;恢复;数据又回来了;同理,如果你在 AOF 文件后面增加了一行 flushall ;那恢复最后,又被 flush 了;

3.2.4. 优劣势

  1. 优势
    • 更好保护数据不丢失,性能高,可以做紧急恢复;
  2. 劣势
    • AOF 文件和 RDB 文件相比,在相同数据集情况下,AOF 文件较大,恢复速度较慢;
    • AOF 运行效率慢于 RDB,每秒同步策略较好,不同步效率与 RDB 相同;

3.3. AOF 重写机制

3.3.1. 是什么

  • 原因:redis 不断将写命令追加到 AOF 文件之中,那么文件会越来越大,占用磁盘空间也会越来越大,恢复时间亦是如此;
  • 解决:redis 增加重写机制,当 AOF 文件大小超过所设定的峰值的时候,会自动启动 AOF 文件的内容压缩,只保留可以恢复数据的最小数据集;或者可以手动执行命令:bgrewriteaof
  • 即:启动 AOF 文件内容压缩,只保留可以恢复数据的最小指令集;

3.3.2. 触发机制

  1. 官网默认配置:

    auto-aof-rewrite-percentage: 100 # 根据上次重写后的 aof 大小,当前是不是增长了一倍
    auto-aof-rewrite-min-size: 64mb # 重写时满足的文件大小
    
    • 同时满足的情况下,才会进行触发;
  2. 手动触发:bgrewriteaof

  3. 触发结果:

    img

3.3.3. 重写原理

  AOF 重写函数会进行大量写操作,如果在主进程中,会导致长时间被阻塞,故在子进程中完成;子进程执行 AOF 重写期间,redis 仍然可以处理命令;子进程带有父进程的内存数据拷贝副本,不使用锁的情况下,依旧可以保持数据安全;因为子进程在 AOF 重写时设置一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程后开始使用;redis 执行完一个写命令,同时将写命令发送给 AOF 缓冲区和 AOF 重写缓冲区;

image-20250208082038161

当子进程完成重写工作后,会向父进程发送一个信号,父进程接受到后,会调用信号处理函数,完成一下操作:

  • 将 AOF 重写缓冲区中的所有内容写入到新的 AOF 文件中,保证新 AOF 文件保存的数据库状态和服务器当前状态一致;
  • 对新的 AOF 文件进行改名,原子覆盖先有 AOF 文件,完成新旧文件替换;
  • 继续处理客户端请求;

整个 AOF 后台重写的过程中,只有信号处理函数执行时会对 redis 主进程造成阻塞,在其他时候,aof 后台重写都不会阻塞主进程;

时间 redis 主进程(父进程) 子进程 是否阻塞
t1 执行命令 NO
t2 执行命令 开始 AOF 重写 NO
t3 创建子进程,执行 AOF 重写 开始 AOF 重写 NO
t4 执行命令 执行重写操作 NO
t5 执行命令 完成重写,向父进程发送信号 NO
t6 接受到信号,将上述两个命令添加到新的 AOF 文件中 YES
t7 用新 AOF 文件覆盖旧 AOF 文件 YES

整个过程不需要读取以前旧的 AOF 文件;

3.4. 详细参数配置

配置参数 配置含义 配置示例
appendonly 是否开启 AOF appendonly yes
appendfilename 文件名 appendfilename “aof.aof”
appendfsync 同步方式 everysec/always/no
no-appendfsync-on-rewrite aof 重写期间是否同步 yes / no
auto-aof-rewrite-percentage , auto-aof-rewrite-min-size 重写触发配置,文件重写策略;达到 100 , 64Mmb

4. AOF + RDB

4.1. 数据恢复顺序和加载流程

1691368296613.jpg

4.2. RDB-AOF 混合

  1. 开启混合方式设置:aof-use-rdb-preamble: yes
  2. RDB-AOF --> RDB 做全量持久化,AOF 做增量持久化
    • 使用 RDB 进行快照存储,再使用 AOF 持久化记录所有写操作,重写策略满足或者手动触发重写的时候,fork 出子进程,会以 RDB 形式将最新的数据存储写入 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,这部分增量命令,会以 AOF 形式写入 AOF 文件;这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能;
    • 混合持久化方式产生的文件一部分是 RDB 格式的全量数据,一部分是 AOF 格式的增量数据

Comment