redis 持久化 AOF和 RDB 引发的生产故障

概要
       最近听开发的同事说,应用程序链接 redis 时老是抛出链接失败或超时之类的错误。经过观察在 redis 日志,发现日志中出现 "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 server 上的处理延迟,并且客户端上也可能发生异常或链接超时。redis

       继续深刻发现,这是由于启用了 AOF(appendonly yes),而且 appendfsync 参数为 everysec,形成的磁盘 I/O 负载高。那么本文将介绍该问题发生的缘由,并给出一些建议。缓存

故障分析bash

       首先咱们要知道,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。而,咱们的生产主库和从库都同时开启了AOF已经RDB save的方式作持久化。网络

       为了更清晰的看到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秒的数据丢失。架构

       网上有小伙伴说,该错误在后续版本有修复。我我的这并非一个BUG,至于真假还请小伙伴验证。app

解决方法async

方法1、各类修改配置ide

最后发现,原来是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

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

尝试修改

echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf  

最后执行 "sysctl -p" 使配置生效;

方法2、关闭 RDB 或 AOF 持久化

经过上面咱们知道该故障问题,其实就是由于磁盘大量IO 形成的请求阻塞。那么禁用 RDB 或 AOF 任一持久化便可,固然,建议禁用 RDB 而使用 AOF。

最后

下面再给你们一些在实际生产中的建议,以及避免这类问题的发生。首先,若是架构容许尽可能不要在主库上同时作 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在这个设置上取决于内核,甚至更少的磁盘压力和延迟尖峰的风险。

至于在工做当中,如何选择,嘿嘿,那就只能结合开发需求和部门领导意见啦。

相关文章
相关标签/搜索