AOF 为什么是在执行完命令之后记录日志?
关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。AOF 记录日志过程为什么是在执行完命令之后记录日志呢?
-
避免额外的检查开销,AOF 记录日志不会对命令进行语法检查;
-
在命令执行完之后再记录,不会阻塞当前的命令执行。
这样也带来了风险(我在前面介绍 AOF 持久化的时候也提到过):
-
如果刚执行完命令 Redis 就宕机会导致对应的修改丢失;
-
可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的)。
AOF 重写
当 AOF 变得太大时,Redis 能够在后台自动重写 AOF 产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。
AOF 重写(rewrite) 是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
由于 AOF 重写会进行大量的写入操作,为了避免对 Redis 正常处理命令请求造成影响,Redis 将 AOF 重写程序放到子进程里执行。
AOF 文件重写期间,Redis 还会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。
开启 AOF 重写功能,可以调用 BGREWRITEAOF 命令手动执行,也可以设置下面两个配置项,让程序自动决定触发时机:
-
auto-aof-rewrite-min-size:如果 AOF 文件大小小于该值,则不会触发 AOF 重写。默认值为 64 MB;
-
auto-aof-rewrite-percentage:执行 AOF 重写时,当前 AOF 大小(aof_current_size)和上一次重写时 AOF 大小(aof_base_size)的比值。如果当前 AOF 文件大小增加了这个百分比值,将触发 AOF 重写。将此值设置为 0 将禁用自动 AOF 重写。默认值为 100。
文件重写(AOF Rewrite)是Redis AOF持久化机制的一种功能,它能够对AOF文件中的历史写命令进行压缩和整理,以便减少文件的大小,并提高访问性能。
当Redis服务器在运行一段时间后,AOF文件会不断增大,导致对磁盘空间的占用和访问性能的下降。为了减少AOF文件的大小和提高访问性能,Redis引入了文件重写功能。AOF Rewrite功能会扫描整个数据库,将执行过的写命令重写到新的AOF文件中,并以一种紧凑且可读性强的格式保存。在重写完成后,新文件将替换旧文件,成为新的AOF文件。
AOF Rewrite功能的执行过程包括以下几个步骤:
-
创建新的AOF文件,开始进行重写操作。
-
Redis使用服务器进程的内部数据结构来重现所有的写操作,将操作逐个写入到新的AOF文件中。
-
Redis使用一个临时缓冲区来保存重写过程中的写操作,避免重写过程中Redis无法响应客户端请求的现象。
-
当Redis重写完所有的写操作之后,它会将新的AOF文件重命名为旧的AOF文件的名字,并将临时缓冲区中的写操作集成到新的AOF文件中。
-
最后,Redis会将旧的AOF文件删除,释放与之关联的磁盘空间。
需要注意的是,AOF Rewrite操作需要消耗大量的处理器和磁盘资源,因此在执行重写操作时,需要权衡磁盘空间占用和访问性能之间的关系,选择适当的重写时间间隔。
Redis 4.0 对于持久化机制做了什么优化?
由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
如何选择 RDB 和 AOF?
RDB 比 AOF 优秀的地方
RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候,RDB 速度更快。
AOF 比 RDB 优秀的地方
RDB 的数据安全性不如 AOF,没有办法实时或者秒级持久化数据。生成 RDB 文件的过程是比较繁重的, 虽然 BGSAVE 子进程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务干宕机。AOF 支持秒级数据丢失(取决 fsync 策略,如果是 everysec,最多丢失 1 秒的数据),仅仅是追加命令到 AOF 文件,操作轻量。
RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
AOF 以一种易于理解和解析的格式包含所有操作的日志。你可以轻松地导出 AOF 文件进行分析,你也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。
综上
Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。
不建议单独使用 AOF,因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。
Redis 过期字典
Redis 过期字典是 Redis 中用于实现 Key 的过期时间的一种数据结构。当一个 Key 设置了过期时间后,在到达过期时间时,Redis 会自动将这个 Key 删除,以释放空间。
在 Redis 中,当 Key 设置了过期时间时,Redis 会将这个 Key 和它的过期时间插入到过期字典中。过期字典是以跳跃表(Skip List)为基础实现的,每个节点包含了 Key 和它的过期时间。
Redis 每秒钟会对过期字典进行检查,以删除已经过期的 Key。检查时,Redis 会对跳跃表中的每个节点,检查它的过期时间是否已经过期,如果已经过期,则执行删除操作,并将 Key 从过期字典中删除。
过期字典的主要作用是自动删除过期 Key,以释放空间。这对于缓存系统非常有用,因为缓存数据通常是有限期的,一旦过期就需要删除它们。进行手动删除显然不可行,因此过期字典的自动删除机制非常有用。
以下是过期字典的核心结构体:
typedef struct redisDb {
// ...
dict *dict; // 字典,保存着数据库中的所有键值对
dict *expires; // 过期字典,保存着键的过期时间
// ...
} redisDb;
其中,dict
变量保存数据库的所有键值对,expires
变量保存键的过期时间,它们都是 dict
类型(哈希表),expires
变量是一个特殊的哈希表类型。
以下是 Redis 中设置 Key 过期时间的命令:
// 设置 key 10 秒后过期
$> SET key value EX 10
// 设置 key 在指定时间戳过期
$> SET key value EXAT 1609459200
以下是 Redis 过期字典的代码示例:
// 设置 key 并设置过期时间为 10 秒
$> SET key value EX 10
// 获取 key 的过期时间
$> TTL key
// 返回 key 是否存在和是否带有过期时间(-1 表示 key 不存在,-2 表示 key 存在但没有设置过期时间)
$> EXISTS key
除了在键过期时自动删除键以外,Redis 的过期键检查还具有一些其他的功能。例如,过期字典还可以提供网络普通键(network ordinary key)的拓展能力,使得这些键不会因为过期而被删除。网络普通键是具有周期性的过期时间的键,它的过期时间不是固定的,而是会根据具体应用场景进行动态调整。网络普通键的过期时间需要由逻辑执行程序(比如定时器)来动态维护。
Redis 过期字典的实现是基于字典结构和跳跃表结构,这样的实现方法可以保证 Redis 的高性能。因为跳跃表具有较好的查找、插入和删除性能,而且 Redis 跳跃表是基于链表实现的,具有空间效率优势。
总结来说,Redis 过期字典是 Redis 实现键的过期时间的一种数据结构。通过使用过期字典,可以实现自动删除过期键的功能。过期字典的实现基于字典结构和跳跃表结构,具有高性能和较好的空间效率。在实际应用中,可以利用过期字典实现缓存系统和网络普通键等应用场景。