背景

Redis是单进程的,为了发挥多核的优势,我们redis集群采用在单台机器上部署多个redis实例,同时对持久化方式做了改进,采用rdb和增量的aof相结合的方式,appendfsync配置为no。那这样会有一个时段同时进行aof和rdb,当aof和rdb同时进行时,监控系统报警说某些请求Timeout,我们超时设置为2s。另外我们的机器分为两种类型,cpu配置不同,一种是4核,另外一种是12核,timeout发生在4核的机器上。 
分析 
   解决这个问题还是走了不少弯路的,所以在这儿和大家分享一下。经过一番分析找到一下可能导致慢的原因

  • CPU,rdb过程会有lzf compress以及crc会消耗cpu
  • bgsave的fork,fork需要copy page entry table到子进程中去,这个过程是阻塞的
  • IO问题,bgsave产生大量的IO,但这是在子进程中完成,应该不会影响主进程吧,在此处埋个伏笔

CPU问题? 
因为timeout的问题主要出现在4核的机器上,12核的机器没有问题,所以首先怀疑cpu问题。我用info commandstats查看各个命令执行时间都是us级别,但4核的比12核的慢了近50%。如果是cpu撑不了,这个值应该至少在ms级别。同时profiler分析了redis执行bgsave的函数耗时。在MakeFile中添加-pg,注意编译和链接都需要加,我开始只在编译阶段加了,没有效果。

  1. GPROF=-pg
  2. FINAL_CFLAGS=$(STD) $(WARN) $(GPROF) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
  3. # redis-server
  4. $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
  5. $(REDIS_LD) $(GPROF)  -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)

观察都lzf和crc确实很占用cpu时间是最多的。crc采用一种增量的方式,而不是每次全量,性能应该很高。那么关闭compress和crc以后是否能解决呢?关闭以后问题依旧看来不是cpu的问题。

IO问题? 
关闭aof以后问题解决,这当中也做了很多尝试,调整bgsave子进程的进程和IO有优先级,将主进程和子进程分别绑定在不同的cpu上,都不管用一度怀疑不是IO的问题。 
在这里介绍几个工具

  • renice 调整进程的优先级
  • ionice 调整进程的IO优先级,当然这是针对block层的IO schedule,为啥不生效,后来才发现,ionice对async write的不起作用,cfq是将async write请求在同一请求队列中不属于进程自己的队列,write(2)一般情况是写入page cache就立马返回,而不是等待真正落入磁盘
  • taskset 将指定进程绑定到指定的cpu上

既然write(2)是buffered IO,写先到page cache中,那究竟是什么阻塞了呢?write是不是无限制地使用pagecache,这一点不清楚,这一段时间就狂看各种linux内核相关文档和看源码。 
发现阻塞write(2)有几种情况: 

  1. fsync(fdatasync) 会阻塞write
  2. pdflush会阻塞write
  3. 还有当dirty page超过可用内存的40%,这时候就会调用schedule_timeout阻塞200ms。write并不是仅仅copy到page cache就返回了,他还会检查dirty page的使用比例,如超过了会尝试阻塞地写入磁盘,如果此时不能完全写入,就会调用schedule_timeout从而产生iotimewait,此时使用top观察确实有90%的CPU使用是iotimewait

弯路 
说说其中的弯路,由于我的粗心放过了一个重大的线索,同事告诉我一个linkhttp://www.redis.io/topics/latency,其中有一节就讲到了aof被大量IO阻塞的问题,作者提到fdatasysnc会阻塞write(2),但我们设置appendfsync为no,不会出现fdatasync和write(2)的阻塞 
那究竟是什么阻塞了write呢,曾一度怀疑是dirty page超过可用内存的40%导致的拥塞等待。 
但后来在一台64G内存redis只占用了7G内存的机器上遇到aof阻塞,排除了拥塞等待,那还有什么原因呢? 
我查看redis的pullrequesthttps://github.com/antirez/redis/pull/1862,发现idning也遇到了类似问题,提出aof采用单独异步线程write的方案,不过作者有点不感冒没有merge,但他提出可以把文件系统的日志模式改为writeback模式不写日志,我试了会有所改观但不会解决问题。我依然知道到底是什么阻塞了write,这时候我把write的代码整体读了一篇然后用systemtap检测每一个可疑的高耗时函数

  1. probe kernel.function("__wait_on_bit_lock").return {
  2. if(isinstr(execname(),"redis-server")){
  3. time = gettimeofday_us() - @entry(gettimeofday_us())
  4. if(time > 100000){
  5. printf("trace:%s,pid:%d,function:%s,cost:%d\n",ctime(gettimeofday_s()),pid(),probefunc(),time);
  6. print_stack(backtrace());
  7. }
  8. }
  9. }

锁定在__wait_on_bit_lock,再根据调用堆栈发现有很多地方调用它,比如是read的时候,等待时候把数据从磁盘加载到page中等等。最终定位到一个可疑之处

  1. page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);

write的时候都会先定位写那个page并尝试lock,而flush dirty page的时候也会先lock住。如果pdflush时,rdb产生大量的IO,那么他有可能尝试占用lock从而 __grab_cache_page等待很长时间。关于这一点我还不是很确定,欢迎大家帮助我确认。 

 
作者原话:那是因为你要求Redis保证fsync语义,RDB没有。 如果你想处理爆发我建议在绝对需要的时候提供延迟fsync超过2秒的技巧
 
解决Latency的正确流程 
Redis的作者在http://www.redis.io/topics/latency,已经总结了很多种latency的情况,遇到latency一定要先看它,是否有负责自己的场景,比如我提的问题就是Latency due to AOF and disk I/O,我开始使用了pstack和strace但是都没有定位到阻塞的原因,其实strace可以定位到,但是使用方式不对导致没有捕获到真正的原因。redis2.6以后还提供了watchdog功能,通过config set watchdog打开该功能,就能在日志中打印出慢查询的堆栈。

Redis的Aof被阻塞原因调查的更多相关文章

  1. Redis深入学习笔记(五)Redis阻塞原因

    在实际使用Redis中,有时会碰到客户端timeout异常,或者没有可用连接异常等等异常,总结大概有如下原因: 内部阻塞原因: 1)大对象存取. 2)Fork阻塞. 3)Aof刷盘阻塞(距离上次刷盘大 ...

  2. 7.Redis主线程阻塞原因

    7.Redis主线程阻塞原因7.1 发现阻塞7.2 内在原因7.2.1 API或数据结构使用不合理7.2.2 CPU饱和7.2.3 持久化阻塞7.3 外在原因7.3.1 CPU竞争7.3.2 内存交换 ...

  3. Redis持久化——AOF(二)

    核心知识点: 1.AOF:以独立日志的方式记录写命令,重启时再执行命令.与RDB不同的是解决数据持久化的实时性,可以记录所有写操作. 2.AOF工作流程:写入命令.文件同步.文件重写.文件加载. 3. ...

  4. Redis - 持久化 AOF 和 RDB

    Redis - 持久化 AOF 和 RDB AOF AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集. AOF 文件中的命令全部以 Redis 协议的格 ...

  5. Redis的AOF功能

    引言:  Redis是基于内存的数据库,同时也提供了若干持久化的方案,允许用户把内存中的数据,写入本地文件系统,以备下次重启或者当机之后继续使用.本文将描述如何基于Redis来设置AOF功能 什么是R ...

  6. Redis开启AOF导致的删库事件

    事件背景 Redis主从开启AOF,错误操作导致数据被清空. Redis主要作用:缓存.队列. 事故过程 Redis搭建了主从,持久化方式为RDB,RDB没有定时备份,且AOF都没有开启. 考虑到开启 ...

  7. 一文了解:Redis的AOF持久化

    Redis的AOF持久化 每当Redis-Server接收到写数据时,就把命令以文本形式追加到AOF文件里,当重启Redis服务时,AOF文件里的命令会被重新执行一次,重新恢复数据.当AOF过大时将重 ...

  8. redis 配置文件aof配置

    redis 配置文件aof配置: bind 127.0.0.1 port 6379 daemonize yes dbfilename dump.rdb dir /new_renpeng/redis/ ...

  9. redis 开启AOF 持久化

    redis 开启AOF 找到redis 安装目录 打开 redis.conf  修改以下参数: appendonly  yes        (默认no,关闭)表示是否开启AOF持久化: append ...

随机推荐

  1. java中的注解详解和自定义注解

    一.java中的注解详解 1.什么是注解 用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数据.比如,下面这段代码: @Override public Str ...

  2. HTML5:绘制图形

    canvas绘图通过属于 canvas 的 JavaScript 方法完成 针对不支持html5的IE浏览器 <!--[if IE]> <script type="text ...

  3. nullptr 与 constexpr

    nullptr   nullptr出现的目的自然是替换NULL的低位.C++可能会将NULL.0视为同一种东西.这取决于编译器是如何定义的,有的编译器定义NULL为 ( (void * )0) ,有的 ...

  4. Key-Value Observing (键值监測)

    Key-Value Observing (键值监測) 简单介绍 KVO是一套当目标对象的属性值改变时观察者对象能够接受到通知的机制.必须先理解KVC才干更好的理解KVO,前者是后者的实现基础. 这种通 ...

  5. PHPCMS V9管理员password忘记怎样改动

    一般的虚拟主机商都提供了PHPmyAdmin,选择你站点数据库.然后选择v9_admin这个表. 编辑 password,变成:fa3250300be9b7ab0848257f3cbb06e7 enc ...

  6. 小心!Ubuntu14.04 升级到16.04 的几个坑

    收录待用,修改转载已取得腾讯云授权 昨天趁着周末把服务器升级了一把,遇到的坑可不少: sudo apt update sudo apt dist-upgrade 坑1:升级失败后,改用下面命令: su ...

  7. netty参考

    前言 问题 现如今我们使用通用的应用程序或者类库来实现系统之间地互相访问,比如我们经常使用一个HTTP客户端来从web服务器上获取信息,或者通过web service来执行一个远程的调用. 然而,有时 ...

  8. css - 公众号样式

    /* * @Author: WJ_LONG * @Date: 2018-09-06 15:32:06 * @Last Modified by: WJ_LONG * @Last Modified tim ...

  9. Git学习资源推荐

    Git在线练习 http://pcottle.github.io/learnGitBranching/ https://try.github.io/levels/1/challenges/1 Git入 ...

  10. 设计模式学习笔记--备忘录(Mamento)模式

    写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...