文件系统:AOF 模式

Redis 会持久化保存数据到磁盘,这个过程要依赖文件系统来完成,所以,文件系统将数据写回磁盘的机制,会直接影响到 Redis 持久化的效率。而且,在持久化的过程中,Redis也还在接收其他请求,持久化的效率高低又会影响到 Redis 处理请求的性能。
 
你可能会问,Redis 是个内存数据库,为什么它的性能还和文件系统有关呢?
我在前面讲过,为了保证数据可靠性,Redis 会采用 AOF 日志或 RDB 快照。其中,AOF日志提供了三种日志写回策略:no、everysec、always。这三种写回策略依赖文件系统的两个系统调用完成,也就是 write 和 fsync。
write 只要把日志记录写到内核缓冲区,就可以返回了,并不需要等待日志实际写回到磁盘;而 fsync 需要把日志记录写回到磁盘后才能返回,时间较长。下面这张表展示了三种写回策略所执行的系统调用。
notion image
当写回策略配置为 everysec 和 always 时,Redis 需要调用 fsync 把日志写回磁盘。但是,这两种写回策略的具体执行情况还不太一样。
在使用 everysec 时,Redis 允许丢失一秒的操作记录,所以,Redis 主线程并不需要确保每个操作记录日志都写回磁盘。而且,fsync 的执行时间很长,如果是在 Redis 主线程中执行 fsync,就容易阻塞主线程。所以,当写回策略配置为 everysec 时,Redis 会使用后台的子线程异步完成 fsync 的操作。
而对于 always 策略来说,Redis 需要确保每个操作记录日志都写回磁盘,如果用后台子线程异步完成,主线程就无法及时地知道每个操作是否已经完成了,这就不符合 always 策略的要求了。所以,always 策略并不使用后台子线程来执行。
另外,在使用 AOF 日志时,为了避免日志文件不断增大,Redis 会执行 AOF 重写,生成体量缩小的新的 AOF 日志文件。AOF 重写本身需要的时间很长,也容易阻塞 Redis 主线程,所以,Redis 使用子进程来进行 AOF 重写。
但是,这里有一个潜在的风险点:AOF 重写会对磁盘进行大量 IO 操作,同时,fsync 又需要等到数据写到磁盘后才能返回,所以,当 AOF 重写的压力比较大时,就会导致 fsync 被阻塞。虽然 fsync 是由后台子线程负责执行的,但是,主线程会监控 fsync 的执行进度。
当主线程使用后台子线程执行了一次 fsync,需要再次把新接收的操作记录写回磁盘时,如果主线程发现上一次的 fsync 还没有执行完,那么它就会阻塞。所以,如果后台子线程执行的 fsync 频繁阻塞的话(比如 AOF 重写占用了大量的磁盘 IO 带宽),主线程也会阻塞,导致 Redis 性能变慢。
为了帮助你理解,我再画一张图来展示下在磁盘压力小和压力大的时候,fsync 后台子线程和主线程受到的影响。
notion image
好了,说到这里,你已经了解了,由于 fsync 后台子线程和 AOF 重写子进程的存在,主IO 线程一般不会被阻塞。但是,如果在重写日志时,AOF 重写子进程的写入量比较大,fsync 线程也会被阻塞,进而阻塞主线程,导致延迟增加。现在,我来给出排查和解决建议。
首先,你可以检查下 Redis 配置文件中的 appendfsync 配置项,该配置项的取值表明了Redis 实例使用的是哪种 AOF 日志写回策略,如下所示:
notion image
如果 AOF 写回策略使用了 everysec 或 always 配置,请先确认下业务方对数据可靠性的要求,明确是否需要每一秒或每一个操作都记日志。有的业务方不了解 Redis AOF 机制,很可能就直接使用数据可靠性最高等级的 always 配置了。其实,在有些场景中(例如 Redis 用于缓存),数据丢了还可以从后端数据库中获取,并不需要很高的数据可靠性。
如果业务应用对延迟非常敏感,但同时允许一定量的数据丢失,那么,可以把配置项 noappendfsync-on-rewrite 设置为 yes,如下所示:
no-appendfsync-on-rewrite yes
这个配置项设置为 yes 时,表示在 AOF 重写时,不进行 fsync 操作。也就是说,Redis 实例把写命令写到内存后,不调用后台线程进行 fsync 操作,就可以直接返回了。
当然,如果此时实例发生宕机,就会导致数据丢失。反之,如果这个配置项设置为 no(也是默认配置),在 AOF 重写时,Redis 实例仍然会调用后台线程进行 fsync 操作,这就会给实例带来阻塞。
如果的确需要高性能,同时也需要高可靠数据保证,我建议你考虑采用高速的固态硬盘作为 AOF 日志的写入设备。
高速固态盘的带宽和并发度比传统的机械硬盘的要高出 10 倍及以上。在 AOF 重写和fsync 后台线程同时执行时,固态硬盘可以提供较为充足的磁盘 IO 资源,让 AOF 重写和fsync 后台线程的磁盘 IO 资源竞争减少,从而降低对 Redis 的性能影响。