redis--28.1--常见问题--Asynchronous AOF fsync is taking too long(disk is busy)

文章讨论了Redis在进行AOF持久化时,由于fsync操作耗时过长导致的客户端超时和服务端‘AsynchronousAOFfsyncistakingtoolong’警告。问题源于大量写入和磁盘性能低下,解决方案包括调整系统内核参数以优化fsync行为,关闭RDB或AOF持久化,以及在生产环境中选择合适的持久化策略。
摘要由CSDN通过智能技术生成

redis–28.1–常见问题–Asynchronous AOF fsync is taking too long(disk is busy)


1、现象

1.1、redis 客户端(应用端)

 
org.redisson.client.RedisTimeoutException: Command still hasn't been written into connection! Check connection with Redis node: 10.202.129.96/10.202.129.96:6379 for TCP packet drops. Try to increase nettyThreads setting.  Node source: NodeSource [slot=0, addr=null, redisClient=null, redirect=null, entry=null], connection: RedisConnection@1165455922 [redisClient=[addr=redis://10.202.129.96:6379], channel=[id: 0x9a938f12, L:/100.100.22.64:39794 - R:10.202.129.96/10.202.129.96:6379], currentCommand=CommandData [promise=java.util.concurrent.CompletableFuture@a8d8ed7[Not completed, 1 dependents], command=(ZREMRANGEBYSCORE), params=[redisson__cache_updates_log:{menuCache}, 0, 1688353276115], codec=org.redisson.client.codec.LongCodec], usage=1], command: (ZREMRANGEBYSCORE), params: [redisson__cache_updates_log:{menuCache}, 0, 1688353276115] after 3 retry attempts
	at org.redisson.command.RedisExecutor$2.run(RedisExecutor.java:200)
	at io.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:672)
	at io.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:747)
	at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:472)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

1.2、redis 服务端

Asynchronous AOF fsync is taking too long (disk is busy?). writing the AOF buffer withoutwaiting for fsynclow down Redis 

2、源码分析

在这里插入图片描述

从代码中分析得知,当Redis后台有fsync操作,并且等待超过了2s,就会阻塞write操作,此时Redis是不可写入的,就会打印出Asynchronous AOF fsync is taking too long (disk is busy?) 日志,触发业务超时。

2.1、下面的情况可能会导致Redis的fsync阻塞2s

  1. 如果开启了 appendfsync everysec 的fsync策略,并且no-appendfsync-on-rewrite参数为no,则redis在做AOF重写的时候,也会每秒将命令fsync到磁盘上,而此时Redis的写入量大而磁盘性能较差,fsync的等待就会严重;

  2. 单纯的写入量大,大到磁盘无法支撑这个写入。例如appendfsync参数的值是everysec,每秒进行一次fsync,而磁盘的性能很差。

3、故障分析

在这里插入图片描述

redis 是一个多路复用的单进程应用程序。多路,指的是多个网络地址,复用是指重复利用单个线程。
当打开 AOF 持久化功能后, Redis 处理完每个事件后会调用 write(2) 将变化写入 kernel 的 buffer,如果此时 write(2) 被阻塞,Redis 就不能处理下一个事件。Linux 规定执行 write(2) 时,如果对同一个文件正在执行fdatasync(2)将 kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被Block住,整个Redis被Block住。

如果系统IO 繁忙,比如有别的应用在写盘,或者Redis自己在AOF rewrite或RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致 fdatasync(2) 迟迟未能完成从而 Block 住 write(2),Block 住整个 Redis。。

为了更清晰的看到fdatasync(2)的执行时长,可以使用 “strace -p (pid of redis server) -T -e -f trace=fdatasync”,但会影响系统性能。Redis提供了一个自救的方式,当发现文件有在执行 fdatasync(2) 时,就先不调用 write(2),只存在 cache 里,免得被 Block。但如果已经超过两秒都还是这个样子,则会硬着头皮执行 write(2),即使 redis 会被 Block 住。此时那句要命的 log 会打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.”。之后用redis-cli INFO 可以看到 aof_delayed_fsync 的值被加1。因此,对于 fsync 设为 everysec 时丢失数据的可能性的最严谨说法是:如果有 fdatasync 在长时间的执行,此时 redis 意外关闭会造成文件里不多于两秒的数据丢失。如果 fdatasync 运行正常,redis 意外关闭没有影响,只有当操作系统 crash 时才会造成少于1秒的数据丢失。

3、解决方案

3.1、方案1:各种修改配置

最后发现,原来是AOF rewrite时一直埋头的调用 write(2),由系统自己去触发 sync。在RedHat Enterprise 6里,默认配置vm.dirty_background_ratio=10,也就是占用了 10% 的可用内存才会开始后台 flush,而我的服务器有 8G 内存。很明显一次 flush 太多数据会造成阻塞,所以最后果断设置 “sysctl vm.dirty_bytes=33554432(32M)” 。AOF rewrite时定时也执行一下 fdatasync 嘛, antirez 回复新版中,AOF rewrite 时 32M 就会重写主动调用 fdatasync。

查看一下系统内核参数 

>sysctl -a | grep dirty_background_ratio
vm.dirty_background_ratio = 10
 
# 查看内存的脏页字节大小,设置为0代表由系统自己控制何时调用fsync

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

尝试修改

 
# 修改为一个小的值,例如32MB,达到这个数据量就fsync,让操作系统fsync这个动作更频繁一点,避免单次fsync太多数据,导致阻塞 
echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf 

最后执行以下命令使配置生效

sysctl -p

3.2、方案2:关闭 RDB 或 AOF 持久化

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

一般建议 禁用 RDB 而使用 AOF。

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

4、实际生产中的建议

避免这类问题的发生。首先,如果架构允许尽量不要在主库上同时做 RBD 和 AOF 持久化(如果要做,就使用SSD固态硬盘)。但是,这样如果不做持久化,将暴露如下问题:

  1. 当 redis 服务挂掉之后,重启缓存全部丢失;
  2. 当主机挂掉,通过脚本或sentinel将从提升为主之后,以前主库就作废;

这样做的好处就是,提升了主库的性能。坏处就是,当主库宕机之后,你只能将该主机从主从当中剔除;

上面这种方式提高了一定的复杂性,那么要解决这个问题。那么可以选择一个折中办法,那就是做AOF持久化而不使用RDB的方式。我们可以通过下面的内容来选择适合自己的持久化方式,如下;

  • RDB。在这里,您可以根据您配置的保存触发器进行大量权衡。
  • AOF + fsync 总是这样:这个速度非常慢,只有当你知道你在做什么的时候才应该使用它。
  • AOF + fsync 每一秒:这是一个很好的妥协。
  • AOF + fsync 每秒+ no-appendfsync-on-rewrite选项设置为yes:如上所示,但避免在重写期间fsync降低磁盘压力。
  • AOF + fsync 永远不会。 Fsyncing在这个设置上取决于内核,甚至更少的磁盘压力和延迟尖峰的风险。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值