Skip to content

从180s到7ms,到底经历了啥?

分类:

前言

众所周知写轮眼是很厉害的一种瞳术,可以看出敌人下一步的动作进而采取相应的防御措施,但是再厉害的术都有弱点,写轮眼也不例外,写轮眼的天敌就是飞雷神,飞雷神也就是拥有超快的速度以至于写轮眼根本看不穿,这样一来写轮眼的施术者也只能任人摆布了,这能看出‘速度’快多么重要了,回归正题,为什么要有这篇文章,问题出现在最近的一次日志服务上线中,大家都知道像这种日志系统应该尽可能的与业务系统解耦,但是在系统设计之初就是因为没有做到完全的解耦将消息发送端放到了日志系统中,导致当日志系统挂了时影响到了业务系统的正常运行,那这和速度快有啥关系了?当我将系统解耦完成重新发布后确实好了,但是日志系统的使用却出奇的慢,数据也有丢失,所以这篇文章就诞生了。

问题排查

当出现bug时我们肯定第一时间去看日志,当我查看完日志后将目标锁定到我代码中查询对应条件的记录上,因为一直在这个位置报read time out,这个时候其实我已经知道了是查询太慢了,于是乎我查看慢查询日志,果然和我想的一样,基本所有查询都是180s左右,这谁忍的了啊,然后我登入mongodb机器,性能监控显示cpu一直100%,这个时候我想机器不会炸了把,这个时候已经知道了问题了所以接下来就是在于怎么优化查询了。

问题解决

解决查询慢这种问题时一般都是往索引方面去想,但是我tm也只知道分析和优化sql啊,对于mongodb确实一点都没接触过啊,领导发话必须优化到秒级以内,那我只能去我的菜鸟教程学一波了,于是我在我瞎折腾一番后有了接下来的成果。

在mongobd中用到的分析工具也是explain()

与大多数关系型数据库一样,mongo也为我们提供了explain方法用于分析一个语句的执行计划。explain支持queryPlanner(仅给出执行计划)、executionStats(给出执行计划并执行)和allPlansExecution(前两种的结合)三种分析模式,默认使用的是queryPlanner,因为我这次使用的是executionStates,所以下面来看看这些executionStates下返回的字段都是什么涵义:

executionStats模式中,我们主要需要注意的返回有如下几个: executionStats.executionSuccess是否执行成功 executionStats.nReturned查询的返回条数 executionStats.executionTimeMillis整体执行时间 executionStats.totalKeysExamined索引扫描次数 executionStats.totalDocsExamineddocument扫描次数 以上几个非常好理解,我们就不在这里详述,后文的案例中会有分析。 executionStats.executionStages.stage这里就是显示以什么形式去扫描对于documents(例如FETCH) executionStats.executionStages.nReturned如果是FETCH,所以这里该值与executionStats.nReturned一致 executionStats.executionStages.docsExamined与executionStats.totalDocsExamined一致 executionStats.inputStage中的与上述理解方式相同 还有一些文档中没有描述的返回如: “works” : 29862, “advanced” : 29861, “isEOF” : 1, 这些值都会在explan之初初始化。 以works为例,查看源码中发现,每次操作会加1,且会把执行时间记录在executionTimeMillis中,而在查询结束EOF,works又会加1,advanced不加。故正常的返回works会比nReturned多1,这时候isEOF为true(1),advanced的返回值在命中的时候+1,在skip,eof的时候不会增加。 上面说到的Stage: Stage的意义如explain.queryPlanner.winningPlan.stage和explain.queryPlanner.winningPlan.inputStage等。 文档中仅有如下几类介绍: COLLSCAN 全表扫描 IXSCAN 索引扫描 FETCH 根据索引去检索指定document SHARD_MERGE 将各个分片返回数据进行merge SORT 表明在内存中进行了排序(与老版本的scanAndOrder:true一致) LIMIT 使用limit限制返回数 SKIP 使用skip进行跳过 IDHACK 针对_id进行查询 SHARDING_FILTER 通过mongos对分片数据进行查询 COUNT 利用db.coll.explain().count()之类进行count运算 COUNTSCAN count不使用用Index进行count时的stage返回 COUNT_SCAN count使用了Index进行count时的stage返回 SUBPLA 未使用到索引的$or查询的stage返回 TEXT 使用全文索引进行查询时候的stage返回 PROJECTION 限定返回字段时候stage的返回

说一千道一万我们在优化过程中上面的参数都要看吗?不用的,只有关注与3个executionTimeMillis,和3个返回项,nReturned,totalKeysExamined与totalDocsExamined,分别代表该条查询返回的条目、索引扫描条目和文档扫描条目。还要就是最重要的Stage参数。

###创建索引createIndex(),查看索引getIndexes(),删除索引dropIndex()###

在后台创建索引db.values.createIndex({open: 1, close: 1}, {background: true}) 查看索引db.values.getIndexes() 删除索引db.values.dropIndex(name)

###实战###

实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。