0%

redis持久化

目录

[TOC]

redis persistence

Redis 的持久化主要有两大机制, 即 AOF (Append Only File) 日志和 RDB 快照

RDB(Redis Database)

内存快照, 就是指内存中的数据在某一个时刻的状态记录

在指定的时间间隔内将内存中的数据集快照写入磁盘, 即快照; 数据恢复时直接将快照文件读入内存中

给哪些内存数据做快照?

为了提供所有数据的可靠性保证, 它执行的是全量快照

Redis 提供了两个命令来生成 RDB 文件, 分别是 save 和 bgsave

  • save: 在主线程中执行, 会导致阻塞
  • bgsave: 创建一个子进程, 专门用于写入 RDB 文件, 避免了主线程的阻塞, 这也是 Redis RDB 文件生成的默认配置

快照时数据能修改吗?

为了保证快照完整性, 主线程只能处理读操作, 因为不能修改正在执行快照的数据, 为了保证Redis 的性能 Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW), 在执行快照的同时, 正常处理写操作 bgsave 子进程是由主线程 fork 生成的, 可以共享主线程的所有内存数据; bgsave 子进程运行后, 开始读取主线程的内存数据, 并把它们写入 RDB 文件

虽然 bgsave 执行时不阻塞主线程, 但是, 如果频繁地执行全量快照, 也会带来两方面的开销

  • 频繁将全量数据写入磁盘, 会给磁盘带来很大压力, 多个快照竞争有限的磁盘带宽, 前一个快照还没有做完, 后一个又开始做了, 容易造成恶性循环
  • bgsave 子进程需要通过 fork 操作从主线程创建出来; 虽然, 子进程在创建后不会再阻塞主线程, 但是, fork 这个创建过程本身会阻塞主线程, 而且主线程的内存越大, 阻塞时间越长; 如果频繁 fork 出 bgsave 子进程, 这就会频繁阻塞主线程了(所以, 在 Redis 中如果有一个 bgsave 在运行, 就不会再启动第二个 bgsave 子进程)

解决: 增量快照, 就是指, 做了一次全量快照后, 后续的快照只对修改的数据进行快照记录, 这样可以避免每次全量快照的开销

比较AOF: 虽然跟 AOF 相比, 快照的恢复速度快(只需要把 RDB 文件直接读入内存, 这就避免了 AOF 需要顺序、逐一重新执行操作命令带来的低效性能问题), 但是, 快照的频率不好把握, 如果频率太低, 两次快照间一旦宕机, 就可能有比较多的数据丢失, 如果频率太高, 又会产生额外开销

混合使用 AOF 日志和内存快照: 内存快照以一定的频率执行, 在两次快照之间, 使用 AOF 日志记录这期间的所有命令操作

在两次快照之间进行的数据修改操作, 用 AOF 日志记录, 等到第二次做全量快照时, 就可以清空 AOF 日志, 因为此时的修改都已经记录到快照中了, 恢复时就不再用日志了

SNAPSHOTTING快照

1
2
3
4
5
6
7
8
9
10
dbfilename dump.rdb   # 默认持久化到dump.rdb文件中 
save 900 1 # 15分钟内保存一次即修改一次
save 300 10 # 5分钟内保存十次即修改十次
save 60 10000 # 1分钟内保存一万次即修改一万次
stop-writes-on-bgsave-error yes # 保存出错停止写入
rdbcompression yes # 数据压缩
rdbchecksum yes # 数据校验
dir ./
config get dir # 获取redis启动路径
save # 手动备份

RDB适合大规模的数据恢复, 而且对数据的完整性和一致性要求不高

AOF(Append Only File)

数据库的写前日志(Write Ahead Log, WAL), 也就是说, 在实际写数据前, 先把修改的数据记到日志文件中(redo log和binlog是在commit之前写的日志),
以便故障时进行恢复; 不过, AOF 日志正好相反, 它是写后日志, “写后”的意思是 Redis 是先执行命令, 把数据写入内存, 然后才记录日志

传统数据库的日志, 例如 redo log(重做日志), 记录的是修改后的数据, 而 AOF 里记录的是 Redis 收到的每一条命令, 这些命令是以文本形式保存

AOF 为什么要先执行命令再记日志?

为了避免额外的检查开销, Redis 在向 AOF 里面记录日志的时候, 并不会先去对这些命令进行语法检查; 所以, 如果先记日志再执行命令的话, 日志中就有可能记录了错误的命令, Redis 在使用日志恢复数据时, 就可能会出错

Redis 使用写后日志这一方式的一大好处是, 可以避免出现记录错误命令的情况; 除此之外, AOF 还有一个好处: 它是在命令执行后才记录日志, 所以不会阻塞当前的写操作

AOF 两个潜在的风险:

如果刚执行完一个命令, 还没有来得及记日志就宕机了, 那么这个命令和相应的数据就有丢失的风险

AOF 虽然避免了对当前命令的阻塞, 但可能会给下一个操作带来阻塞风险; 这是因为, AOF 日志也是在主线程中执行的, 如果在把日志文件写入磁盘时, 磁盘写压力大, 就会导致写盘很慢, 进而导致后续的操作也无法执行了

以日志的形式记录每个写操作, 将Redis所有的写指令记录下来(不记录读操作), 只许追加文件不许修改文件, 相当于MySQL的脚本文件, 当Redis启动时就会加载appendonly.aof文件将写指令重新执行一遍来恢复数据

AOF 三种写回策略

  • always, 同步写回: 每个写命令执行完, 立马同步地将日志写回磁盘

  • everysec, 每秒写回: 每个写命令执行完, 只是先把日志写到 AOF 文件的内存缓冲区, 每隔一秒把缓冲区中的内容写入磁盘

  • no, 操作系统控制的写回: 每个写命令执行完, 只是先把日志写到 AOF 文件的内存缓冲区, 由操作系统决定何时将缓冲区内容写回磁盘

img

AOF 重写机制

产生原因:

AOF 是以文件的形式在记录接收到的所有写命令, 随着接收的写命令越来越多, AOF 文件会越来越大, AOF 文件过大会带来性能问题

  • 文件系统本身对文件大小有限制, 无法保存过大的文件
  • 如果文件太大, 之后再往里面追加命令记录的话, 效率也会变低
  • 如果日志文件太大, 整个恢复过程就会非常缓慢

AOF 重写机制就是在重写时, Redis 根据数据库的现状创建一个新的 AOF 文件, 也就是说, 读取数据库中的所有键值对, 然后对每一个键值对用一条命令记录它的写入

旧日志文件中的多条命令, 在重写后的新日志中变成了一条命令(AOF 文件是以追加的方式, 逐一记录接收到的写命令的; 当一个键值对被多条写命令反复修改时, AOF 文件会记录相应的多条命令; 但是, 在重写的时候, 是根据这个键值对当前的最新状态, 为它生成对应的写入命令)

注意, AOF的重写不是根据原有的AOF去做, 而是根据当前内存数据库的数据, 去生成一条条命令进行保存

和 AOF 日志由主线程写回不同, 重写过程是由后台子进程 bgrewriteaof 来完成的, 这也是为了避免阻塞主线程, 导致数据库性能下降

APPEND ONLY MODE

1
2
appendonly no  # 默认关闭 yes即代表打开aof的持久化
appendfilename "appendonly.aof"
1
redis-check-aof --fix appendonly.aof # 修复appendonly.aof文件

dump.rdb文件和appendonly.aof文件可以同时存在,当二者同时存在时优先加载appendonly.aof文件

1
2
3
# appendfsync always # 同步持久化 每当有数据发生变化时就会记录到磁盘 数据完整性好但是性能差
appendfsync everysec # 出厂默认 异步操作 每秒记录数据 1s内宕机 数据丢失
# appendfsync no

关于 AOF 和 RDB 的选择问题

  • 数据不能丢失时, 内存快照和 AOF 的混合使用是一个很好的选择;
  • 如果允许分钟级别的数据丢失, 可以只使用 RDB;
  • 如果只用 AOF, 优先使用 everysec 的配置选项, 因为它在可靠性和性能之间取了一个平衡

欢迎关注我的其它发布渠道