我们知道 Redis 之所以读写快、性能高,得益于它是一种基于内存的数据库,毫无疑问它的操作都几乎都是基于内存。但是内存型数据库也有一个很大的弊端:如果进程崩溃或者服务重启的时候内存数据得不到保存,就会造成数据丢失。为了解决这个问题 Redis 提供了两种持久化方式 RDB 和 AOF 。今天笔者主要和大家一起探讨 Redis 的 AOF 模式是如何运行的。
AOF 详解
AOF 是 Append Only File 的缩写,默认是关闭的。当 Redis 启动时只要开启 AOF 模式就会通过 AOF 文件恢复数据,否则将从 RDB 快照文件中恢复数据。开启 AOF 模式需要将 redis.conf 中默认的 appendonly no 改为 appendonly yes 。AOF 开启后 Redis 内部会维护一份 AOF 文件,AOF 文件可以看做是 Redis 的日志文件。Redis 服务使用其通信协议的格式将更新命令保存到 AOF 文件中,所以文件内容容易被人读懂,我们在维护时能够轻松的对文件进行分析。
工作原理
Redis 内部维护了一个缓冲区 aof_buf ,Redis 服务如果发现有更新命令,这些更新命令不是被直接写入到 AOF 文件中的,而是会被先以 Redis 通信协议的格式追加到 aof_buf 中(命令追加),维护 aof_buf 缓冲区的同时会执行 appendfsync 策略(写入和同步策略)。
持久化的过程
了解 appendfsync 策略之前我们先要搞清楚 AOF 持久化的过程,它一共有三步:
1)命令追加:更新命令追加写入到 aof_buf 中。
2)文件写入:执行 write 操作,写入到系统缓冲区。
3)文件同步:从系统缓冲区同步到磁盘中。
同步策略(appendfsync)
1)always:Redis 每次执行更新命令将 aof_buf 中内容写入并同步到 AOF 文件中。 该策略每次执行更新命令触发,每次发生数据变更就会立刻同步到到磁盘。
2)everysec: 将 aof_buf 缓冲区所有内容写入到 AOF 文件,如果上次同步时间距离现在超过1秒钟,那么再次对 AOF 文件进行同步,这个操作是由一个线程专门负责执行的。everysec 策略持久化性能较好,但是宕机时会造成数据丢失。
3)no:将 aof_buf 中的内容写入到 AOF 文件中,但并不对 AOF 同步,何时同步由操作系统来决定。
我们可以看到三种策略都是将 aof_buf 缓冲区的所有内容写入到 aof 文件,只是同步时操作不一样。在操作系统中,除非设置了自动同步,否则为了减少磁盘的写入量,延长磁盘寿命、提高文件写入效率。写入文件时并不是同步写入到磁盘中,而是先写入内存,这段内存区域被称为系统缓冲区。在系统缓冲区数据累计到一定数量后(具体数量因系统实际设置而定),会有系统进程一次性写入所有缓冲数据。因此只有等到同步执行完毕这是才是持久化完成。
always 的优点是数据安全,效率太低,因为过于频繁的调用系统;只有在这种策略下,redis的事务是满足持久化的。
no 的优点是效率较高,但是不够安全,因为 aof_buf 中的数据可能因为宕机而丢失。
ererysec 介于两者之间,在效率和安全之间达到一个平衡。这里我们又一次看到折中思想在计算机中的应用。
AOF 的重写机制
AOF 文件由于不断写入,会变得越来越大。通过分析 AOF 文件,往往发现里面有太多的重复和冗余数据,重写策略就是成一个新的 AOF 文件来代替旧的 AOF 文件,这个操作在满足一定条件 Redis 会自动触发。
当 AOF 文件大小从 0 增长到指定大小时,会触发 AOF 的第一次 rewrite 操作,Redis 会 fork 出来一个进程(这样主进还可以程继续处理命令,fork出来的子进程带有主进程的数据副本,可以在避免锁的情况下,保证数据的一致性),通过 bgRewriteAOF 命令完成:基于当前数据生成新 AOF 文件。在此期间 Redis 如果收到新的更新命令,Redis 会将命令暂时保存在内存。等待新的 AOF 文件生成后,Redis 会将内存中的命令追加到新 AOF 文件中。新 AOF 完成后,Redis 会删除旧的 AOF 文件。当新的 AOF 文件扩大到2倍的时候会触发第二次 rewrite。
在重写期间,主进程继续处理命令,而新的命令有可能还会对现在有数据进行修改,这会导致当前数据库中的数据和生成的 AOF 文件不一致。Redis 维护了一个 AOF 重写缓冲区 rewrite_buf ,主进程接收到更新命令后,把命令写入到 rewrite_buf 中。这样主进程在接收到跟新命令的时候一边将命令写入 aof_buf ,(重写执行是)一边写入到 rewrite_buf 中(重写的时候会保持继续更新旧 AOF 文件)。
fork 出来的子进程完成 bgRewriteAOF 操作后,会向主进程发送信号,会执行一个信号处理函数 1)将 rewrite_buf 中的数据全量写到新的 AOF 文件中。2)重命名新 AOF 文件,覆盖旧文件。信号处理函数完成时主进程会短暂阻塞。
AOF 优势劣势有哪些呢?
优势
1)AOF 模式带来了更高数据安全性的数据持久化解决方案。
2)AOF 模式对日志文件的写入操作采用的是 append 模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。如果我们数据写入了一半出现了宕机现象 redis-check-aof 工具可以帮我们解决问题,感兴趣同学们可以去研究下。
3)如果日志过大,Redis 有自动启动的 rewrite 机制。即 Redis 以 append 模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行 rewrite 切换时可以更好的保证数据安全性。
4) AOF 包含一个格式清晰、易于理解的日志文件用于记录所有的更新操作。事实上,我们也可以通过该文件完成数据的重建。
劣势
1)由于 AOF 格式是 Redis 通信协议并且文件是命令 append 因此通常要大于 RDB 模式下产生的 .rdb 文件。RDB 模式产生的是数据快照文件小,海量数据集时 RDB 模式恢复速度会比 AOF 快。
2)同步策略与 RDB 模式,AOF 在运行效率上往往会慢于 RDB 。但是,everysec 同步策略的效率也是是比较高的,no 同步策略的效率和RDB一样高效。
总结:
Redis 的两种持久化机制各有优劣,AOF 模式是牺牲性能换取数据数据一致性, RDB 模式则是牺牲一致性换取更高性能。同学们选型时需要根据业务场景合理的使用。
想了解 RDB 持久化模式的同学可以关注下,下回再一起聊一聊~
点击收藏、关注不迷路,欢迎大家评论区留言讨论~