redis debug asynchronous aof fsync is taking too long (disk is busy?)

redis debug asynchronous aof fsync is taking too long (disk is busy?)

jerichou
2022-12-01 / 0 评论 / 17 阅读 / 正在检测是否收录...

一.背景
线上redis的监控数据不太好看,领导觉得看看能不能优化下,分析下什么情况,于是就.....

二.现象
看到redis日志里面一堆这个,于是就开始了分析排查
lb569q5d.png

先放一张前后对比图吧
lb57o9ok.png

三.排查
之前在网上找,也看到有人说了相关的解释,不过还是自己决定在多研究下其他的缘由,直接去官网 看看官方的解释 redis troubleshoting
好早之前分析的,现发现,redis的官网变了,这玩意都找不到,直接把之前的图贴上来吧

lb57esms.png

重点看红框部分,此项警告在redis代码中是在AOF fsync刷盘模式是everysecs,代码中的注释 /* Otherwise fall trough, and go write since we can't wait over two seconds,redis中的AOF持久化主要是又两个系统调用实现,一个是fdatasync,另外一个是write,redis发现文件有在执行 fdatasync(2) 时,就先不调用 write(2),只存在 cache 里,免得被 Block。但如果已经超过两秒都还是如此,则会强行执行 write(2),导致redis 会被 Block 住 。

居然知道了大概的方向,我们就直接去看日志然后再通过strace去监控write和fdatasync系统调用时间来进行排(线上还是谨慎使用strace吧,这玩意系统调用还是很危险的,优选ebpf)
此时观测redis日志,发现

redis 主线程 PID:2418

redis AOF线程 PID:2412 (服务器配置的AOF同步策略是everysec,故每秒执行一次)

redis RDB子进程 PID:14603

lb57gci8.png
lb57gpkx.png

有了这个日志和数据,我们直接开启硬核的步骤,去看redis的源码,看看RDB和AOF的执行流

redis执行流分析
lb57hcm5.png

redis进程是一个事件循环,分为文件事件和时间事件

文件事件:主要对套接字操作的抽象,服务器通过监听并处理这些事件来完成一系列网络通信的操作,完成客户端的命令请求和回复

时间事件:主要分为周期的和定时的事件,做一些统计、清理、aof等的工作

因为服务器在处理文件事件时可能会执行命令,使得一些内容被追加到aof_buf缓冲区,所以服务器每次执行一次事件循环都会调用flushAppendOnlyFile函数,考虑要不要将aof_buf缓冲区中的内容写入保存到AOF文件中。
lb57i6pr.png

RDB后台同步流程
lb57is5l.png
lb57jcdc.png

rdbSave将数据写入同步到磁盘,write写完文件之后,进行了(fflush,fsync)强行刷盘,导致IO繁忙
lb57k185.png
lb57kpbu.png

RDB后台子进程发送退出信号给父进程,父进程捕获然后处理
lb57l7eo.png

redis主进程AOF 刷盘逻辑
lb57lpk9.png

根据上一次刷盘的延时,来决定要不要return,如果此时延时超过2s则将延迟加1,打出log,接着主进程为了保证AOF的数据一致性,需要将AOF写命令写入到AOF缓冲区,强行调用write
lb57m87f.png

如果当前没有再做AOF_FSYNC的后台任务,根据设置的sync策略进行调用FSYNC刷盘
lb57n4oy.png

四.解决方案
方法一:配置修改

Redis在执行write的时候,由操作系统自身被动控制何时进行fsync。这里如果我们要主动触发操作系统的fsync,可以设置操作系统级别的参数:

查看内存的脏页字节大小,设置为0代表由系统自己控制何时调用fsync

sysctl -a | grep vm.dirty_bytes
vm.dirty_bytes = 0

修改为一个小的值,例如32MB,达到这个数据量就fsync,让操作系统fsync这个动作更频繁一点,避免单次fsync太多数据,导致阻塞

echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf  
方法二:关闭RDB或者AOF(安全性换效率)

关闭RDB可减少RDB生成的时候,对磁盘的压力,而关闭AOF则可以减少AOF刷盘对磁盘的压力,从而让Redis的性能达到极致。不过这个方法只能用在纯缓存场景,如果有持久化的要求,则一般不靠谱,因为安全性大大降低,一旦宕机,则数据无法恢复。

一种折中的方案,是从库开启AOF,而主库关闭AOF。

1

评论 (0)

取消