目录
[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 | dbfilename dump.rdb # 默认持久化到dump.rdb文件中 |
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 文件的内存缓冲区, 由操作系统决定何时将缓冲区内容写回磁盘
AOF 重写机制
产生原因:
AOF 是以文件的形式在记录接收到的所有写命令, 随着接收的写命令越来越多, AOF 文件会越来越大, AOF 文件过大会带来性能问题
- 文件系统本身对文件大小有限制, 无法保存过大的文件
- 如果文件太大, 之后再往里面追加命令记录的话, 效率也会变低
- 如果日志文件太大, 整个恢复过程就会非常缓慢
AOF 重写机制就是在重写时, Redis 根据数据库的现状创建一个新的 AOF 文件, 也就是说, 读取数据库中的所有键值对, 然后对每一个键值对用一条命令记录它的写入
旧日志文件中的多条命令, 在重写后的新日志中变成了一条命令(AOF 文件是以追加的方式, 逐一记录接收到的写命令的; 当一个键值对被多条写命令反复修改时, AOF 文件会记录相应的多条命令; 但是, 在重写的时候, 是根据这个键值对当前的最新状态, 为它生成对应的写入命令)
注意, AOF的重写不是根据原有的AOF去做, 而是根据当前内存数据库的数据, 去生成一条条命令进行保存
和 AOF 日志由主线程写回不同, 重写过程是由后台子进程 bgrewriteaof 来完成的, 这也是为了避免阻塞主线程, 导致数据库性能下降
APPEND ONLY MODE
1 | appendonly no # 默认关闭 yes即代表打开aof的持久化 |
1 | redis-check-aof --fix appendonly.aof # 修复appendonly.aof文件 |
dump.rdb文件和appendonly.aof文件可以同时存在,当二者同时存在时优先加载appendonly.aof文件
1 | appendfsync always # 同步持久化 每当有数据发生变化时就会记录到磁盘 数据完整性好但是性能差 |
关于 AOF 和 RDB 的选择问题
- 数据不能丢失时, 内存快照和 AOF 的混合使用是一个很好的选择;
- 如果允许分钟级别的数据丢失, 可以只使用 RDB;
- 如果只用 AOF, 优先使用 everysec 的配置选项, 因为它在可靠性和性能之间取了一个平衡