一.背景
线上redis的监控数据不太好看,领导觉得看看能不能优化下,分析下什么情况,于是就.....
二.现象
看到redis日志里面一堆这个,于是就开始了分析排查
先放一张前后对比图吧
三.排查
之前在网上找,也看到有人说了相关的解释,不过还是自己决定在多研究下其他的缘由,直接去官网 看看官方的解释 redis troubleshoting
好早之前分析的,现发现,redis的官网变了,这玩意都找不到,直接把之前的图贴上来吧

重点看红框部分,此项警告在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


有了这个日志和数据,我们直接开启硬核的步骤,去看redis的源码,看看RDB和AOF的执行流
redis执行流分析 
redis进程是一个事件循环,分为文件事件和时间事件
文件事件:主要对套接字操作的抽象,服务器通过监听并处理这些事件来完成一系列网络通信的操作,完成客户端的命令请求和回复
时间事件:主要分为周期的和定时的事件,做一些统计、清理、aof等的工作
因为服务器在处理文件事件时可能会执行命令,使得一些内容被追加到aof_buf缓冲区,所以服务器每次执行一次事件循环都会调用flushAppendOnlyFile函数,考虑要不要将aof_buf缓冲区中的内容写入保存到AOF文件中。
RDB后台同步流程

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

RDB后台子进程发送退出信号给父进程,父进程捕获然后处理
redis主进程AOF 刷盘逻辑
根据上一次刷盘的延时,来决定要不要return,如果此时延时超过2s则将延迟加1,打出log,接着主进程为了保证AOF的数据一致性,需要将AOF写命令写入到AOF缓冲区,强行调用write
如果当前没有再做AOF_FSYNC的后台任务,根据设置的sync策略进行调用FSYNC刷盘
四.解决方案
方法一:配置修改
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。
评论 (0)