参考在前, 尊重知识产权
- 几个监控工具
- 优化的几点原则
其实并不是单单的mongo性能优化, 这里记录了整个问题的分析过程.
问题背景:
Mongo是开始接触数据处理的一个窗口, 为什么用mongo? 省事, 稳定, 能力强就直接上了, 所以我们从一开始收集数据的基调就定了, 规范数据上报结构, 通过统一的server接口收集业务信息, 虽然mongo是NoSQL, 不过设计上一样对结构比较考究, 主要是为了后续使用分析方便, 添加版本管理. 并且基于mongo, django, matplot设计了第一个项目, 使用mongo的mapreduce功能, 和django的简易部署使用, 用matplot做可视化. 可以做到只编辑map&reduce&finally脚本, 简单加工过滤后, 由django直接显示, 并且可以很方便的和报表系统对接. 这个系统支撑了一年多的业务分析需求, 并且现在还在发挥作用.
之前总结的太少, 一开头就想多说, brake. 真正的背景, 之前一直是用的2.6, 性能和分析使用都没有大问题, 因为只收集有分析价值的规范化数据加上合理的元信息管理, 所以这块的数据增长可控. 不过一个多月前, 由于服务器监控的bug, 导致磁盘写满, 服务宕机, 并且部分数据损坏, 所以索性升级到了3.4, 主要考虑3.4的性能有大的提升, 并且从全局锁变成了表级别的锁
如图, 是3.0和2.6的一个比较, 其实3.2开始, mongo做了非常大的调整, 以至于不兼容老版本的数据, 无法平滑的迁移. 对我们来说, 大部分有价值的分析已经作为中间分析结果存储在mysql或者es里, 所以可以暂时考虑不迁移老数据, 所以这次事故是一个很好的契机.
ps: 下图里多线程写对性能的提升还是比较有限的, 亲测
我们这块的数据流结构是这样的, 这块两年没改动了.
$Cloud --> AMQ --> Consumer --> MongoDB
问题发生在周一, 由ES提供的数据检索服务发现分布式MCU的监控数据严重缺失, 部分不需要实时处理的数据同步延迟也很大,
如图, 这是分布式MCU的健康数据, 理论上数量不会在时间轴上有太大的变动.
问题的分析:
首先, 判断是基于mongo的ETL清洗逻辑出了问题, 导致丢失或者更新不及时. 所以去任务调度部分查看日志, 发现每次的采样计算耗时在5 ~ 10分钟, 这个任务设计上需要保证在1分钟内完成, 否则数据堆积, 不能正确采样数据, 延迟数据直接丢弃. 计算的时间主要消耗在mongo侧, 所以首先查看mongo的数据写入审计监控.
有部分异动数据, 不过不应该会影响性能, 没有大规模的新数据产生.
于是, 需要查看mongo在干什么, 这里有三个分析性能的重要工具或者方法, 开头参考链接里应该有
一是, 通过设计profile查看慢请求, 比如把运算超过100ms以上的都记录下来
二是, mongostat, 是了解mongod整体工作情况的重要工具, 应该不定期的去看看, 了解下运行状态, 3.4的输出做了一些调整, 看官方文档最好, --> mongostat
三是, mongotop, 会以collections为单位, 记录耗时操作时间, 排序, 默认一秒钟一次. --> mongotop
进一步的分析, 上面几个工具都用了, 抱歉没有保留截图, 最终发现耗时操作只在dmcu的计算上, 所以还需要重看一下计算代码, 虽然之前稳定运行过一年多.
直接说问题吧, 注意到上图的查询条件, 看到这条log的时候, 就突然想到, 时间戳没有索引, 事实证明确实没有索引. 那么为什么之前工作没有问题, 想必是刚切到3.4后没有什么数据, 只顾欣喜升级成功吧. 谈到索引, 多说一点应该注意的, 关于时间的所以最好建立倒排索引, 因为毕竟我们的分析通常来说, 在时间轴上, 越近的越有价值, 倒排理论上可以提高业务分析效率.
到此, 可以判定这是一个低级的失误, 不过我们业务上遇到的很多重大问题又有几个不是回过头来看 "低级" 的呢? 添加索引之后, 时间从10分钟降到10秒钟.
不过问题并没有结束, 数据的延迟依然很严重.
现象, 类似上图, 有堆积的命令, 但是没有实际插入数据, 貌似大量的插入请求被堆积了起来, 等着什么, 或者等风来. 过一段时间后, 就会爆发性的插入大量的数据, 这样的数据流失完全不能被后端业务使用的, 貌似也是个让人崩溃的问题, 因为之前用低版本根本没遇到过.
那现在只能看插入数据, 也就是consumer的代码, 发现之前用的insert接口被摒弃了, 已经不建议使用.
并且, acknowledge write被默认设置成了true. 因为写入性能定会很差, 而且根据之前描述的现象貌似用这个接口mongo侧会有bug.
所以这里需要改写. 改写之前测试了一下性能, 发现200条数据, 四个进程, 平均要用10 ~ 15秒, 这个性能远小于之前的测试性能.
这块代码的重构重要涉及了两个方面, 一个是多线程的使用, 另一个是批量插入接口的使用, 并且需要兼顾数据延迟的控制, 目前的策略是小于10秒. 重构之后, 相同的测试条件提升了30 ~ 50倍. 这块数据的处理又顺畅了.
Tips:
Insert_many, 有个参数是ordered, 对于统计分析类的数据, 顺序并不重要, 而且我们定义的数据是自带时间戳的, 如果ordered=true的话, 每个collection的数据插入需要等待上一条完成. 每次插入的数据量, 一般不大于1000, 因为内部实现分批插入, 比如传入2000条, 会分两批插入.
总结:
就这件事来说主要是两个优化最有意义, 索引和多进程批量插入的使用.
其它的优化, 其实还有很多, 系统上的读写分离, 存储引擎的选择等, 硬件上的IO优化, 内存优化等
Mongo在所设计的系统里, 是一个类似hdfs的角色, 用于存储结构化日志, 提供离线分析和查询, 虽然以后会考虑引入其他的存储框架, 来容纳更大规模的, 更灵活的日志数据, 但是目前看都可以和mongo有效的结合, 相得益彰, 所以会一直维护和挖掘他的潜力. 稍后会分享一下如何和其它组件配合使用.
欢迎大家一起讨论.