精华内容
下载资源
问答
  • MongoDB慢日志分析

    2017-03-22 10:30:43
    在 MySQL中,查询日志是经常作为我们优化数据库的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是Mongo Database Profiler.不仅有,而且还有一些比MySQL的Slow Query Log更详细的信息。它就是...

    转自:http://blog.csdn.net/m18580239676/article/details/48371867

    在 MySQL中,慢查询日志是经常作为我们优化数据库的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是Mongo Database Profiler.不仅有,而且还有一些比MySQL的Slow Query Log更详细的信息。它就是我们这篇文章的主题。

      开启 Profiling 功能

      有两种方式可以控制 Profiling 的开关和级别,第一种是直接在启动参数里直接进行设置。

      启动MongoDB时加上–profile=级别 即可。

      也可以在客户端调用db.setProfilingLevel(级别) 命令来实时配置。可以通过db.getProfilingLevel()命令来获取当前的Profile级别。

    > db.setProfilingLevel(2); 
      {"was" : 0 , "ok" : 1} 
      > db.getProfilingLevel()

      上面斜体的级别可以取0,1,2 三个值,他们表示的意义如下:

      0 – 不开启

      1 – 记录慢命令 (默认为>100ms)

      2 – 记录所有命令

      Profile 记录在级别1时会记录慢命令,那么这个慢的定义是什么?上面我们说到其默认为100ms,当然有默认就有设置,其设置方法和级别一样有两种,一种是通过添加–slowms启动参数配置。第二种是调用db.setProfilingLevel时加上第二个参数:

    db.setProfilingLevel( level , slowms ) 
      db.setProfilingLevel( 1 , 10 );

      查询 Profiling 记录

      与MySQL的慢查询日志不同,Mongo Profile 记录是直接存在系统db里的,记录位置 system.profile ,所以,我们只要查询这个Collection的记录就可以获取到我们的 Profile 记录了。

    > db.system.profile.find() 
      {"ts" : "Thu Jan 29 2009 15:19:32 GMT-0500 (EST)" , "info" : "query test.$cmd ntoreturn:1 reslen:66 nscanned:0 
      query: { profile: 2 } nreturned:1 bytes:50" , "millis" : 0} 
      db.system.profile.find( { info: /test.foo/ } ) 
      {"ts" : "Thu Jan 29 2009 15:19:40 GMT-0500 (EST)" , "info" : "insert test.foo" , "millis" : 0} 
      {"ts" : "Thu Jan 29 2009 15:19:42 GMT-0500 (EST)" , "info" : "insert test.foo" , "millis" : 0} 
      {"ts" : "Thu Jan 29 2009 15:19:45 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 reslen:102 nscanned:2 
      query: {} nreturned:2 bytes:86" , "millis" : 0} 
      {"ts" : "Thu Jan 29 2009 15:21:17 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 reslen:36 nscanned:2 
      query: { $not: { x: 2 } } nreturned:0 bytes:20" , "millis" : 0} 
      {"ts" : "Thu Jan 29 2009 15:21:27 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 exception bytes:53" , "millis" : 88}

      列出执行时间长于某一限度(5ms)的 Profile 记录:

    > db.system.profile.find( { millis : { $gt : 5 } } ) 
      {"ts" : "Thu Jan 29 2009 15:21:27 GMT-0500 (EST)" , "info" : "query test.foo ntoreturn:0 exception bytes:53" , "millis" : 88}

      查看最新的 Profile 记录:

      db.system.profile.find().sort({$natural:-1})

      Mongo Shell 还提供了一个比较简洁的命令show profile,可列出最近5条执行时间超过1ms的 Profile 记录。

      Profile 信息内容详解:

      ts-该命令在何时执行.

      millis Time-该命令执行耗时,以毫秒记.

      info-本命令的详细信息.

      query-表明这是一个query查询操作.

      ntoreturn-本次查询客户端要求返回的记录数.比如, findOne()命令执行时 ntoreturn 为 1.有limit(n) 条件时ntoreturn为n.

      query-具体的查询条件(如x>3).

      nscanned-本次查询扫描的记录数.

      reslen-返回结果集的大小.

      nreturned-本次查询实际返回的结果集.

      update-表明这是一个update更新操作.

      fastmod-Indicates a fast modify operation. See Updates. These operations are normally quite fast.

      fastmodinsert – indicates a fast modify operation that performed an upsert.

      upsert-表明update的upsert参数为true.此参数的功能是如果update的记录不存在,则用update的条件insert一条记录.

      moved-表明本次update是否移动了硬盘上的数据,如果新记录比原记录短,通常不会移动当前记录,如果新记录比原记录长,那么可能会移动记录到其它位置,这时候会导致相关索引的更新.磁盘操作更多,加上索引更新,会使得这样的操作比较慢.

      insert-这是一个insert插入操作.

      getmore-这是一个getmore 操作,getmore通常发生在结果集比较大的查询时,第一个query返回了部分结果,后续的结果是通过getmore来获取的。

      MongoDB 查询优化

      如果nscanned(扫描的记录数)远大于nreturned(返回结果的记录数)的话,那么我们就要考虑通过加索引来优化记录定位了。

      reslen 如果过大,那么说明我们返回的结果集太大了,这时请查看find函数的第二个参数是否只写上了你需要的属性名。(类似 于MySQL中不要总是select *)

      对于创建索引的建议是:如果很少读,那么尽量不要添加索引,因为索引越多,写操作会越慢。如果读量很大,那么创建索引还是比较划算的。(和RDBMS一样,貌似是废话 -_-!!)

      MongoDB 更新优化

      如果写查询量或者update量过大的话,多加索引是会有好处的。以及~~~~(省略N字,和RDBMS差不多的道理)

      Use fast modify operations when possible (and usually with these, an index). See Updates.

      Profiler 的效率

      Profiling 功能肯定是会影响效率的,但是不太严重,原因是他使用的是system.profile 来记录,而system.profile 是一个capped collection 这种collection 在操作上有一些限制和特点,但是效率更高。

    http://blog.zol.com.cn/3044/article_3043984.html


    展开全文
  • 说到MongoDB慢日志分析,就不得不提到profile分析器,profile分析器将记录的慢日志写到system.profile集合下,这个集合是一个固定集合。我们可以通过对这个集合的查询,来了解当前的慢日志,进而对数据库进行优化...
  • mongodb分析工具安装(https://www.cnblogs.com/littleatp/p/9114471.html,https://blog.csdn.net/xiewen99/article/details/52452328) yum -y install glibc-devel python-devel yum -y install python-pip ...
    mongodb分析工具安装(https://www.cnblogs.com/littleatp/p/9114471.html,https://blog.csdn.net/xiewen99/article/details/52452328)
    yum -y install glibc-devel python-devel
    yum -y install python-pip              
    yum -y install python-pymongo          
    pip install mtools                     
    yum install tkinter                      
    yum -y install xorg-x11-xauth
    
    pip安装(参照https://pip.pypa.io/en/latest/installing/#using-the-installer)
    curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
    python get-pip.py

     

    展开全文
  • 记一次 MongoDB 慢日志优化历程 原创YL网易游戏运维平台2019-08-24 YL 运维开发工程师,负责游戏系统配置管理平台的设计和开发,目前专注于新 CMDB 系统的开发,平时也关注运维自动化,DevOps,Python 开发等...

    记一次 MongoDB 慢日志优化历程

    原创 YL 网易游戏运维平台 2019-08-24

     

    YL

    运维开发工程师,负责游戏系统配置管理平台的设计和开发,目前专注于新 CMDB 系统的开发,平时也关注运维自动化,DevOps,Python 开发等技术。

    背景

    CMDB 为了使用事务来存储机器的数据,启用了 mongodb4.0 版本,在平均 1.5k qps 并发写的情况下(这只是机器层面的数据,机器的里面有很多子资源的更新,每个子资源的更新会对应一个 mongodb 操作),mongodb 一直处于高负载状态,导致很多操作变得很慢,从慢日志的统计来看,严重的时候,一小时可以产生 14w+ 条慢日志,使得数据消费的速度下降,导致队列出现堆积,优化迫在眉睫。优化的方向主要有两个,一个在业务层面控制数据的写入速度,一个是在数据库端尝试进行优化,提高数据库的写入性能。本篇文章主要聚焦在数据库层面的优化。

    mongodb 索引简介

    为了方便理解后面的优化思路,先简单介绍 mongodb 的索引,但不会太详细,只会涉及到本次优化中使用到的索引类型。

    mongodb 的索引类型分为:

    • 单键索引(Single Field Index)

    • 复合索引(Compound Index)

    • 多键索引(Multikey Index)

    • 地理空间索引(Geospatial Index)

    • 文本索引(Text Indexes)

    • 哈希索引(Hashed Indexes)

    如果我们想要定义某个索引为唯一索引,可以使用索引的属性来定义,索引的属性有:

    • 唯一索引

    • 部分索引

    • 稀疏索引

    • TTL 索引

    galaxyx 存储机器资源的集合,主要使用了单键索引(唯一索引),复合索引,多键索引,以下的内容只会涉及到这三种索引,其他索引的介绍请参考 官方文档。

    索引的存储

    mongodb 索引使用 B-Tree 数据结构来存储,B-Tree 的每个节点都存放创建索引的 key 的值  (value),以及该值对应文档的存储位置信息(mmapv1 和 wiredTiger 生成位置信息的方式不同),存储引擎再通过该位置信息从磁盘中读取对应的文档数据。这种存储方式和 mysql 的非聚集索引类似,不同的是 mysql 索引使用 B+Tree ,只有叶子节点才存放数据,如果使用 innodb 引擎,叶子节点上存放的对应行的 primary key 的值,查找任何一行数据的磁盘 IO 次数与索引的树高度相同,而 mongodb 索引全部节点都可以存储数据,最好的情况下只用进行一次磁盘 IO,最坏的情况也是和索引树高度相同。

    下面通过一个例子来解释 mongodb 的索引结构,比如有一个集合(users),文档存放着用户的名字(name),年龄(age),孩子(childrens), 测试数据如下:

    1{"name": "a", "age": 30, "childrens": [{"name": "a_a", "age": 3}, {"name": "a_b", "age": 1}]}
    2{"name": "b", "age": 30, "childrens": [{"name": "b_a", "age": 2}]}
    3{"name": "c", "age": 32, "childrens": [{"name": "c_a", "age": 4}, {"name": "c_b", "age": 1}]}
    4{"name": "e", "age": 33, "childrens": [{"name": "e_a", "age": 5}, {"name": "e_b", "age": 2}]}
    5{"name": "f", "age": 32, "childrens": [{"name": "f_a", "age": 4}, {"name": "f_b", "age": 1}]}
    6{"name": "d", "age": 40, "childrens": [{"name": "d_a", "age": 10}]}
    7{"name": "g", "age": 42, "childrens": [{"name": "g_a", "age": 12}, {"name": "g_b", "age": 8}]}
    

    经过存储引擎持久化之后,集合的每个文档都拥有一个存放的位置信息,可以理解为超市寄存柜的编号。

    如果对 age 创建一个单键索引:

    1db.users.createIndex({'agen': 1})
    

    那么索引的数据如下所示:

    1age   位置               磁盘上的文档
    230    position3   ->    {"name": "a", "age": 30, "childrens": [{"name": "a_a", "age": 3}, {"name": "a_b", "age": 1}]}
    330    position5   ->    {"name": "b", "age": 30, "childrens": [{"name": "b_a", "age": 2}]}
    432    position1   ->    {"name": "c", "age": 32, "childrens": [{"name": "c_a", "age": 4}, {"name": "c_b", "age": 1}]}
    532    position2   ->    {"name": "f", "age": 32, "childrens": [{"name": "f_a", "age": 4}, {"name": "f_b", "age": 1}]}
    633    position4   ->    {"name": "e", "age": 33, "childrens": [{"name": "e_a", "age": 5}, {"name": "e_b", "age": 2}]}
    740    position6   ->    {"name": "d", "age": 40, "childrens": [{"name": "d_a", "age": 10}]}
    

    age 是索引节点上的 key,位置是索引节点的 value,代表磁盘中文档存放的位置。

    如果对 name 和 age 创建一个复合索引:

    1db.users.createIndex({'name': 1, 'agen': 1})
    

    那么索引的数据如下:

    1name,age   位置      
    2a,30    position3 
    3b,30    position5 
    4c,32    position1
    5f,32    position2
    6e,33    position6
    7d,40    position4
    

    如果对 age 和 childrens 的 name 字段创建一个复合索引,因为 childrens 是一个数组,存储引擎会自动转化为多键索引,索引的数据如下:

     1age,childrens.name    位置
     230,a_a                position3
     330,a_b                position3
     430,b_a                position1
     532,c_a                position2
     632,c_b                position2
     732,f_a                position5
     832,f_b                position5
     933,e_a                position4
    1033,e_b                position4
    1140,d_a                position6
    

    多键索引将数组里面每个元素都提取出来作为索引的 key,一旦数组的元素很多时,将会有多个 key 指向同一个文档,容易带来索引过大的问题,毕竟插入或者删除一个节点的数据,其他节点的数据将会移动或者分裂,所以需要考虑更新数组元素带来的性能消耗。

    由上可以总结出,索引中 key 的数量和文档数量的比例为:

    索引类别比例
    单键索引或复合索引1:1
    多键索引N:1

    以上只是简单的展示索引排序后存储的结构,真实的数据应该是以 B-Tree 的结构存放。通过索引很容易查询到符合条件的数据,可见索引的重要性。但是否索引创建越多越好呢?

    慢日志之痛

    在背景我们也提到,在业务高并发写入的情况,1 小时的慢日志会有 14w 之多,严重降低了数据消费速度。所以从慢日志开始这次的优化之路。

    先开启 profiling 功能,此处已把需要优化的 DB 的 profiling 级别设置为 1,设置的命令为 db.setProfilingLevel(1),默认执行时间大于 100ms 的操作命令都会被记录。

    有两种方式可以查看慢日志,一种是直接查看日志文件(文件的位置和配置有关,此处是 /home/ocean/log/mongodb,每天产生一个新的日志文件),另一张是查看 db.system.profile 这个 collection。

    在开始优化前,我们先了解一下慢日志里面包含了哪些信息,从这些信息中可以看出什么问题。

    慢日志的格式如下:

    12019-05-16 16:35:07 10.192.xx.xx  2019-05-16T16:35:07.071+0800 I COMMAND  [conn80762] command galaxyx.cr_ipv4 command: find { find: "cr_ipv4", filter: { ip: "10.xx.xx.xx" }, projection: { _id: 1 }, limit: 1, singleBatch: true, lsid: { id: UUID("2652b715-ce8e-402c-9ff9-976b8b444dc5") }, $clusterTime: { clusterTime: Timestamp(1557995685, 2), signature: { hash: BinData(0, 1CC1672BDAEFC2EFA5DBABF9EF3A0CE3C26EEC34), keyId: 6678850264708939806 } }, $db: "xxx", $readPreference: { mode: "primary" } } planSummary: IXSCAN { ip: 1 } keysExamined:1 docsExamined:1 cursorExhausted:1 numYields:0 nreturned:1 reslen:424 locks:{ Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 }, acquireWaitCount: { r: 1 }, timeAcquiringMicros: { r: 13148462 } }, Collection: { acquireCount: { r: 1 } } } protocol:op_msg 13148ms
    

    主要包含以下几种数据:

    • 执行操作的类型:command,insert,query,update,remove,getmore。

    • 执行的具体操作:如

      1command: find { find: "cr_ipv4", filter: { ip: "10.xx.xx.xx" }, projection: { _id: 1 }, limit: 1, singleBatch: true, lsid: { id: UUID("2652b715-ce8e-402c-9ff9-976b8b444dc5") }, hash: BinData(0, 1CC1672BDAEFC2EFA5DBABF9EF3A0CE3C26EEC34), keyId: 6678850264708939806 } }, <span class="katex-html" aria-hidden="true" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.69444em;vertical-align:0em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">c<span class="mord mathit" style="margin-right:0.01968em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">l<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">u<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">r<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">T<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">m<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.69444em;vertical-align:0em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">c<span class="mord mathit" style="margin-right:0.01968em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">l<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">u<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">r<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">T<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">m<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:1em;vertical-align:-0.25em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">T<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">m<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">m<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">p<span class="mspace" style="margin-right:0.16666666666666666em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.16666666666666666em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="margin-right:0.03588em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">g<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">n<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">u<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">r<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.69444em;vertical-align:0em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">h<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">h<span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:1em;vertical-align:-0.25em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.05017em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">B<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">i<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">n<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">D<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">a<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">a<span class="mspace" style="margin-right:0.16666666666666666em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mord mathit" style="margin-right:0.05017em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">B<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">D<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">A<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">F<span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">F<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">A<span class="mord mathit" style="margin-right:0.02778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">D<span class="mord mathit" style="margin-right:0.05017em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">B<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">A<span class="mord mathit" style="margin-right:0.05017em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">B<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">F<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">F<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">A<span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.07153em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">C<span class="mspace" style="margin-right:0.16666666666666666em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.03148em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">k<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">e<span class="mord mathit" style="margin-right:0.03588em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">y<span class="mord mathit" style="margin-right:0.07847em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">I<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">d<span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mspace" style="margin-right:0.2777777777777778em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;word-wrap: inherit !important;word-break: inherit !important;">E"xxx", $readPreference: { mode: "primary" } } 
      2</span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.07847em;"></span class="mord mathit" style="margin-right:0.03588em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.03148em;"></span class="mspace" style="margin-right:0.16666666666666666em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.05017em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.05017em;"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit" style="margin-right:0.05017em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mord mathit" style="margin-right:0.07153em;"></span class="mspace" style="margin-right:0.16666666666666666em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.05017em;"></span class="strut" style="height:1em;vertical-align:-0.25em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="strut" style="height:0.69444em;vertical-align:0em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.03588em;"></span class="mord mathit"></span class="mord mathit"></span class="mspace" style="margin-right:0.16666666666666666em;"></span class="mspace" style="margin-right:0.16666666666666666em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.13889em;"></span class="strut" style="height:1em;vertical-align:-0.25em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.01968em;"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="strut" style="height:0.69444em;vertical-align:0em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mspace" style="margin-right:0.2777777777777778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.02778em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit" style="margin-right:0.01968em;"></span class="mord mathit"></span class="strut" style="height:0.69444em;vertical-align:0em;"></span class="katex-html" aria-hidden="true">
      

      这其中我们只关注前面执行什么命令就好,如上面执行的是 ip 过滤查询的操作。

    • 执行计划(重要):如

      1planSummary: IXSCAN { ip: 1 } keysExamined:1 docsExamined:1 cursorExhausted:1 numYields:0 nreturned:1 reslen:424 
      

      主要关注是否命中索引,扫描的文档的数量。

    • 锁相关的信息:

      1locks:{ Global: { acquireCount: { r: 1 } }, Database: { acquireCount: { r: 1 }, acquireWaitCount: { r: 1 }, timeAcquiringMicros: { r: 13148462 } }, Collection: { acquireCount: { r: 1 } } }
      

      如果 acquireCount 的数量很大,需要考虑读写并发量大小的问题

    • 执行时间(重要):13148ms

    排查思路

    主要优化思路如下:

    1. 先从执行时间最长的日志开始查找问题并解决

    2. 服务再次运行一段时间后,查看是否还有以上问题,如果没有减少过滤时间继续查找问题

    3. 忽略系统相关的慢日志,只关注与业务相关的慢日志

    执行时间最长的日志

    过滤出执行时间大于 10s 的慢日志,发现有很多查询操作是全表扫描,planSummary 为 COLLSCAN,如:

    排查出所有执行全表扫描的查询,在线添加索引,这里需要注意的是,在线添加索引务必要设置为在 background 添加,默认的方式会锁表,阻塞所有的跟添加索引的表相关的读写操作,对于线上业务来说这绝对是不能接受的。

    降低执行时间继续排查

    解决没有添加索引导致查询全表扫描的情况后,继续运行服务一段时间,过滤日志发现已经没有了全表扫描的查询,也没有执行时间在 10s 以上的操作,mongodb 的 CPU 平均使用率也下降了一点:

    但统计下来发现慢日志的数量还是很大。进一步排查,将过滤时间减少到 1s,统计后发现主要有以下三种慢日志:

    • 与事务相关的慢日志

    • system session 相关的日志

    • 与更新机器资源相关的日志

    对于前面两种日志,与 mongo 本身的机制相关,暂时没法下手优化,也可能是由于更新频繁引发,如果解决掉业务相关操作的慢日志,这种慢日志是否会自己消除呢?

    带着这个疑问,将优化的焦点放在第三种日志上,截图的操作是在更新机器上 docker0 这个网卡的数据,看到 planSummary 为 IXSCAN,说明查询命中了索引。我们在初始化表的时候,给 cr_resource 创建了一个唯一索引,以及众多的复合索引,因为有些复合索引的字段类型是数组,在创建索引时 mongodb 会自动转化为多键索引。对于以上日志中的查询操作,原本预想的是,会命中 {'uuid': 1, 'interface.name':1 }这个复合索引,但是命中的索引却是表的唯一索引,见图上的 planSummary:IXSCAN { uuid: 1 }。这说明 {'uuid': 1, 'interface.name':1 } 这个复合索引变成了冗余索引,查询的时候没有用到,但是在插入或者删除数据的时候,这个复合索引上的 B-Tree 部分数据将会移动,甚至会引起节点分裂,这必然会产生一定的性能消耗,如果索引数量很少,这种变动带来的性能消耗微乎其微,但冗余索引且又是多键索引的数量很多,就得令当别论了。

    于是尝试将 cr_resource 的冗余索引全部去除,排查发现该表有 28 个索引,其中有 17 个冗余的复合索引,且都是多键索引,有些机器的网卡有上百个,这样子索引上的 key 和文档的比例将是 100+ :1,可见此类多键索引的节点之多。

    清除冗余索引之后的结果:CPU 使用率从最高 70% 降到 10%,如下图,优化效果明显。

    让人惊喜的是,跟数据库本身机制相关的慢日志也减少了,可见这部分是受到了业务操作的影响。

    总结一下此次优化过程中两个主要关注点:

    • 找出全表扫描的查询,建立索引

    • 删除冗余索引

    疑难杂症

    为什么不会命中复合索引

    在 mongo shell 中执行如下的查询命令,并使用 explain 查看执行计划:

    1db.cr_resource.find({'uuid': 'EC29D450-B5A9-FBE1-DD1E-xxxxxxxxxxx', 'cpu.handle': '0x0401'}).explain()
    

    explain 结果:

    预想情况下,这个查询语句应该会命中  {‘uuid’: 1, ‘cpu.handle’:1 } 这个复合索引,但从 explain 的结果可以看到,显示命中了 {‘uuid’: 1} 这个唯一索引,最后在 FETCH 阶段再过滤出符合 {'cpu.handle': '0x0401’} 的文档。

    个人觉得因为查询条件需要扫描的文档数量为 1,使用唯一索引查询也可以得到相同的结果,且唯一索引的大小比 {‘uuid’: 1, ‘cpu.handle’:1 } 这个复合索引小很多,因为唯一索引上的 key 和文档是 1:1 的关系,复合索引的 key 数量可能是文档数量的几倍甚至几十倍,取决于机器上有多少同类的资源,比如这里是 CPU,如果每台机器上有 30 个 CPU(现实当然不是这样),那么索引上 key 的数量就为 30 * 文档数,所以在唯一索引上查找到匹配文档速度会更快。

    如果复合索引中没有包含唯一键,同样的查询是否会被命中呢?我们把 {‘uuid’: 1} 去掉唯一索引属性,变成一个普通的单键索引,然后插入相同的 uuid 的机器记录,相同的查询 explain 结果如下:

    可见确实命中了复合索引,但仔细一想,某种程度上来说,{‘uuid’: 1} 这个索引已经变成了 {‘uuid’: 1, ‘cpu.handle’:1 } 的子集,所有只查询 uuid 的操作都可以使用后者索引,uuid 的单键索引就变成了冗余索引了。

    update 操作为什么会引发高负载

    cr_resource 集合里面的文档已经涵盖大部分机器的数据,后面只用进行 update 操作。对于每个集合来说,插入文档和删除文档必然会导致索引的更新,如果索引很多,会带来性能消耗,但是只是更新文档,是否也会如此呢?

    如果数据库的引擎是 MMAPv1,当更新文档以至于文档的大小超出已分配的空间时,引擎就会将该文档转移到一个更大的空间存储,同时更新文档所有的索引。

    但我们使用的是 wiredTiger 存储引擎,没有使用以上的存储机制,按照官方的解释,部分 update 也会触发索引更新,但此处没有发现是哪些的更新操作引发索引更新。(后面再找时间深入调研)

    在优化的时候,我们还发现了一个现象,使用 db.serverStatus().globalLock 查看 cr_resource 表的锁状态的时候,发现最大写并发数(globalLock.activeClients.writers)最高接近 100,此时 mongodb CPU 使用率也达到高峰,wiredTiger 默认限制传递到引擎层面的最大读写并发数均为 128,所以有可能是并发数量过大,某些请求长时间占有锁,机器资源的更新使用事务,事务内部可能会对多个表执行读写操作,如果多个事务更新同一个表及索引,就会引发等待锁和抢占锁的问题,进一步增大 mongodb 机器的负载,在优化索引之后,写并发数最高不超过 10 个。

    总结

    以上我们针对 mongodb 的慢日志,梳理了优化的过程,并介绍了 mongodb 索引相关的知识,以及自己的一些见解。关于创建索引以及索引优化的一些建议:

    • 了解业务是使用场景,有针对性地创建索引,创建过多的索引会消耗数据库的性能。

    • 如果使用唯一键来查询,可以不用创建包含唯一键的复合索引

    • 查询条件涵盖多个键,按照最左前缀原则创建复合索引,比如{’name’: 1, ‘age’: 1, ’tel’: 1} 可以覆盖以下查询:
          {'name': 'xx’, age: xx, 'tel': 'xxx'}
          {'name': 'xx', age: xx}
          {'name': 'xx'}

    • 创建多键索引时,需要考虑数组的大小对更新索引引发的性能问题。

    • 某个操作执行慢的时候,应考虑是否跟索引相关。

    • 学会使用 explain 查看执行计划。

    参考

    • WiredTiger Storage Engine

      (https://docs.mongodb.com/manual/core/wiredtiger/)

    • Write Operation Performance

      (https://docs.mongodb.com/manual/core/write-performance/#document-growth-and-the-mmapv1-storage-engine)

    • Effective MongoDB Indexing

      (https://dzone.com/articles/effective-mongodb-indexing-part-1)

    • MongoDB serverStatus.globalLock 深入解析

      (https://yq.aliyun.com/articles/201983)

    • MongoDB Indexing Best Practices

      (https://www.compose.com/articles/mongodb-indexing-best-practices/)

       

     

    往期精彩

    NEW

    KSM 应用实践

     

    S3 的中文编码问题及修复方案

     

    通用实时日志分类统计实践

     

    从清档需求谈谈 Redis 二级索引的使用

     

    Swap 与 Swappiness

    展开全文
  • mongodb-慢日志分析

    2021-07-08 22:07:54
    mtools-你可能没用过的mongodb神器 转载:mongodb可以通过profile来监控数据 ...开启慢日志 开启Profiling功能 mongodb可以通过profile来监控数据,进行优化。 getProfilingLevel() 通过命令db.getProfilingLevel.

    mtools-你可能没用过的mongodb神器
    转载:mongodb可以通过profile来监控数据 (mongodb性能优化)
    其他一些mtools用法: 使用Mtools分析MongoDB日志文件

    在MySQL中,通过慢查询日志作为性能优化的主要切入点,SQL优化步骤+慢SQL分析
    Mongo中也有类似的功能

    开启慢日志

    开启Profiling功能

    mongodb可以通过profile来监控数据,进行优化。
    getProfilingLevel()
    通过命令db.getProfilingLevel()查看profiling开启状态:

    • 0代表关闭
    • 1代表记录慢命令,
    • 2代表全部

    setProfilingLevel(level)
    通过db.setProfilingLevel(level)设置profiling等级,level等级值同上。
    level为1的时候,慢命令默认值为100ms,可以通过如db.setProfilingLevel(1,50),慢日志就设置为50ms

    配置文件开启
    可以通过配置文件,配置profilling

    systemLog:
        destination: file
        path: "/data/mongodb/logs/mongod.log" #慢日志存放目录
    logAppend: true 
    
    operationProfiling:
       slowOpThresholdMs: 100  # 100ms
       mode: slowOp
    

    启动命令配置
    mongod --profile=1 --slowms=200

    查看慢日志-集合

    通过db.system.profile.find()查看当前的监控日志。

    > db.system.profile.find({millis:{$gt:500}})
    {"ts":ISODate("2011-07-23T02:50:13.941Z"),"info":"query order.order reslen:11022 nscanned:672230  \nquery: { status: 1.0 } nreturned:101 bytes:11006 640ms","millis":640}
    {"ts":ISODate("2011-07-23T02:51:00.096Z"),"info":"query order.order reslen:11146 nscanned:672302  \nquery: { status: 1.0, user.uid: { $gt: 1663199.0 } }  nreturned:101 bytes:11130 647ms","millis":647}
    

    这里值的含义是:

    • ts:命令执行时间
    • info:命令的内容
    • query:代表查询
    • order.order: 代表查询的库与集合(命名空间)
    • reslen:返回的结果集大小,byte数
    • nscanned:扫描记录数量
    • nquery:后面是查询条件
    • nreturned:返回记录数及用时
    • millis:所花时间

    如果发现:时间比较长,那么就需要作优化。

    • nscanned数很大,或者接近记录总数,那么可能没有用到索引查询
    • reslen很大,有可能返回没必要的字段
    • nreturned很大,那么有可能查询的时候没有加限制,加分页限制

    日常使用的慢日志(system.profile)查询命令

    # 删除慢日志集合
    db.system.profile.drop()
    
    5 日常使用的慢日志(system.profile)查询
    
    #返回最近的10条记录
    db.system.profile.find().limit(10).sort({ ts : -1 }).pretty()
    
    #返回所有的操作,除command类型的
    db.system.profile.find( { op: { $ne : ‘command‘ } }).pretty()
    
    #返回特定集合
    db.system.profile.find( { ns : ‘mydb.test‘ } ).pretty()
    
    #返回大于5毫秒慢的操作
    db.system.profile.find({ millis : { $gt : 5 } } ).pretty()
    

    mtools分析

    mtools是什么

    mtools 是由MongoDB 官方工程师实现的一套工具集,可以很快速的日志文件查询分析、统计功能,此外还支持本地集群部署管理,非常便于新手学习。
    mtools使用python编写完成,可通过pipy网站 获取;
    该工具包含了以下几个关键组件:

    • mlaunch:支持快速搭建本地测试环境,可以是单机、副本集、分片集群。
    • mlogfilter:日志过滤组件,支持按时间检索慢查询、全表扫描操作,支持通过多个属性进行信息过滤,支持输出为JSON格式。
    • mplotqueries:支持将日志分析结果转换为图表形式,依赖于tkinter(python图形模块)、和matplotlib模块。
    • mlogvis:支持将日志分析结果转换为一个独立的HTML页面,实现与mplotqueries同样的功能。

    1.简易集群管理(略)

    执行以下命令,可以启动一个单节点的mongod进程。

    # mlaunch init --single
    launching: "mongod" on port 27017
    

    2. 日志统计

    总体信息
    mloginfo 是一个用于做日志信息统计的工具,输入以下命令:

    # mloginfo mongo.log
         source: mongo.log
           host: MongoDB_1:10001
            start: 2018 May 18 16:33:11.692
            end: 2018 May 19 01:13:08.290
    date format: iso8601-local
         length: 144480
         binary: mongod
        version: 3.4.10
        storage: wiredTiger
    

    可以看到日志的起止时间范围、主机端口、版本、数据库引擎等概要信息。

    连接数
    当我们希望检查客户端的连接数情况时,可以执行以下命令:

    # mloginfo mongo.log --connections
    CONNECTIONS
         total opened: 14282
         total closed: 14358
        no unique IPs: 4
    socket exceptions: 0
    127.0.0.1 opened: 12886 closed: 12889
    172.21.0.29 opened: 658 closed: 716
    172.21.0.28 opened: 461 closed: 490
    172.21.0.27 opened: 277 closed: 263
    

    通过这样的信息,进一步判断是否存在连接过载等异常情况。

    事件统计
    又或者,你希望统计出当前某些事件的发生频次。

    # mloginfo mongo.log --distinct
    DISTINCT
       14358 end connection ... ( ... now open)
       14281 connection accepted from ... # ... ( ... now open)
       13075 received client metadata from ... :
        5340 Successfully authenticated as principal ... on
        1194 Use of the aggregate command without the 'cursor'
         338 build index on: ... properties:
         244 building index using bulk method; build may temporarily use up to ... megabytes of RAM
         234 ns: ... key: ... name:
         219 Refreshing chunks for collection ... based on version
         218 Refresh for collection ... took ... ms and found version
         179 Index ... :
    

    慢查询
    在业务问题分析中,慢查询是最常见的问题。

    # mloginfo mongo.log --queries --sort count
    QUERIES
    namespace                  operation      pattern                              count      min (ms)    max (ms)  mean (ms)    95%-ile (ms) sum (ms)
    nsspace.StatisticsHour   find             {"$and": [{"recordTime": 1}]..}     22331      276       747          345          414.0            7720736
    nsspace.StatisticsHour   getmore       {"aggregate": 1, "cursor": ...}]}    231          200      304          227          272.0             52587
    dmspace.DeviceInfo      remove          {"_id": 1}                              109        205      1786          420          771.0            45860
    cmspace.DeviceData     update          {"appId": 1, "deviceId": 1}          95          201      1802          431          824.5            40966
    dmspace.TaskHistory    update          {"_id": 1}                                54          268      2643          692          2019.0          37413
    nsspace.StatisticsDay     find              {"$and": [{"recordTime": 1}], ..}   31          201      348            241          345.0            7472
    

    如上面的命令,将显示所有慢查询,并按出现次数排序

    重启信息

    # mloginfo mongo.log --restart
    RESTARTS
       May 18 21:37:51 version 3.4.10
       May 18 21:48:33 version 3.4.10
    

    通过检测重启信息,对系统潜在的故障进行评估分析。

    副本集切换
    同样,主备切换可能导致一定的业务失败,需要定期监测。

    # mloginfo mongo.log --rsstate
    RSSTATE
    date host state/message
    ()
    May 18 21:48:53 172.21.0.29:10001 ARBITER
    May 18 21:49:26 172.21.0.28:10001 SECONDARY
    

    3. 日志过滤

    mlogfilter是一个强大的日志过滤模块,相比linux 的grep/egrep的文本过滤,该组件可以对日志内容进行解析,并按我们想要的结果进行过滤。

    查看超过10s的慢操作

    # mlogfilter mongo.log --slow 10000 --shorten 200
    

    查看慢扫描操作
    慢扫描是指该操作需要扫描过多的记录(超过1w行),且返回数量不足扫描数量的1/100,这样的操作通常对CPU消耗很高,也比较低效,

    # mlogfilter mongo.log --scan --shorten 200
    

    根据名称空间过滤

    # mlogfilter mongo.log --namespace dmspace.DeviceInfo
    

    根据操作类型过滤

    # mlogfilter mongo.log --namespace dmspace.DeviceInfo
    

    根据操作类型过滤

    # mlogfilter mongo.log --operation update
    

    获取某时间点之后1小时的日志

    # mlogfilter mongo.log --from Apr 6 0:00 --to "+1h" | tail -n3
    

    mlogfilter提供了非常灵活的日期条件设置,除了可以指定起始、结束时间之外,还能通过偏移量划分范围。

    时区转换

    # mlogfilter mongo.log --tiemzone 2 > mongo-correct.log
    

    以上命令将日期调大2个时区,输出到mongo-correct.log,这在处理国际化系统的场景中非常有用。

    4. 图表呈现

    mplotqueries 是基于tkinter实现的图表组件,可以将日志中扁平的文字信息转换为图表形式。

    操作时长-散列图
    输入以下命令:

    mplotqueries mongo.log --group operations --output-file operations.png
    

    你可以得到一个按操作分组输出的散点图,如下图:
    在这里插入图片描述
    左侧的Y轴是duration,即操作的执行时长,下边的X轴是时间。每个操作在图中都会有一个描点,因此散点图会存在许多重叠。

    按名称空间进行分组(限显示20个)-扫描记录数量
    当然,你也可以通过集合名称进行分组输出,如下面的命令:

    mplotqueries mongo.log --group namespace --group-limit 20 --type scatter --yaxis nscanned --output-file namespace_nscan.png
    

    输出的图表将按名称空间进行分组(限显示20个),y轴为nscanned值,即扫描记录数量
    在这里插入图片描述
    默认情况下,y轴的呈现为时长(during),可指定为其他指标:

    指标名称说明
    nscanned扫描数
    nupdated更新数
    ninserted插入数
    ntoreturn返回数
    numYields让步次数
    r读锁
    w写锁

    某个时间段,某类操作或某个集合的操作占比
    有时候你并不关系具体的某个操作,而是希望看到某个时间段,某类操作或某个集合的操作占比
    比如每小时,每个集合的操作比例分布,此时可以采用直方图

    mplotqueries mongo.log --group namespace --bucketsize 3600 --group-limit 10 --type histogram --output-file namespaces_perhour.png
    

    在这里插入图片描述

    连接变更统计
    在前面已经讲过,连接数的监测工作非常重要,mplotqueries也提供了连接变更统计类型

    mplotqueries mongo.log --type connchurn --bucketsize 3600 --output-file connchurn_perhour.png
    
    

    在这里插入图片描述
    扫描数/返回数
    在大部分情况下,低效的操作往往来自大量的scan扫描,尤其当return数远小于scan时会更加低效。
    可以通过指定 nscanned/n参数输出该维度的图表,即扫描数/返回数

    mplotqueries mongo.log --type nscanned/n --group-limit 20 --output-file nscan.png
    
    

    在这里插入图片描述

    输出事件持续图
    往往在跟踪某一类耗时操作或事件时需要用到,比如oplog的同步、创建索引等,我们希望看到事件的执行时段。
    同时还需要伴随一些操作统计,用于配合做资源监控分析。具体一点就是,你执行了一个耗时操作,在某些情况下对业务访问可能
    产生了影响,如业务访问超时并伴随DB服务器资源的告警,如CPU飙高,在后续的分析中希望通过耗时操作、以及同时段业务访问的分布进行综合分析。

    以下的命令展示了一个典型用法

    # 创建一个overlay
    grep "index" mongo.log | mplotqueries --type durline --overlay
    # 叠加overlay输出图表
    mplotqueries mongo.log --group operation --output-file duration.png
    

    在这里插入图片描述

    mlogvis

    如果不希望生成那么多的图表,mtools还提供了一个偷懒的工具 mlogvis。可以直接生成html页面,内置强大的脚本.基本上覆盖了mplotqueries的绝大多数图表功能。

    # mlogvis mongo.log
    

    在这里插入图片描述

    生成的html出错,需要将head中 d3.js替换为<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

    安装mtools

    安装python

    wget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz
    
    $ cd Python-3.6.8/
    $ ./configure --with-ssl # 带上ssl不然pip会出现错误
    $ make
    $ make install
    

    安装pip3

    yum install python-tools
    
    pip3 install psutil
    pip3 install pymongo
    pip3 install matplotlib
    pip3 install numpy
    

    安装mtools

    pip3 install mtools
    
    展开全文
  • 在MySQL中,查询日志是经常作为我们优化查询的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是开启Profiling功能。该工具在运行的实例上收集有关MongoDB的写操作,游标,数据库命令等,可以在数据库...
  • MongoDB慢日志查询

    千次阅读 2019-06-13 16:37:50
    说到MongoDB慢日志分析,就不得不提到profile分析器,profile分析器将记录的慢日志写到system.profile集合下,这个集合是一个固定集合。我们可以通过对这个集合的查询,来了解当前的慢日志,进而对数据库进行优化...
  • 在MySQL中,查询日志是经常作为我们优化查询的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是开启Profiling功能。该工具在运行的实例上收集有关MongoDB的写操作,游标,数据库命令等,可以在...
  • 工具研发者介绍贺春旸,凡普金科爱钱进DBA团队负责人,《MySQL管理之道:性能调优、高可用与监控》第一、二版...工具下载:https://github.com/hcymysql/mongo_slowquery简介MongoDBSQL日志是记录到业务库的s...
  • MongoDB日志浅析

    千次阅读 2019-09-01 22:31:18
    文章目录一、系统日志二、Journal日志三、oplog主从日志查询日志3.1 固定集合3.2 oplog主从日志3.3 查询日志四、总结   MongoDB中主要有四种日志。分别是系统日志、Journal日志、oplog主从日志查询日志...
  • 要识别查询,离不开MongoDB内置的剖析器。剖析功能默认是关闭的,需要先打开。在MongoDB Shell中,输入以下命令: 剖析总是针对某个特定数据库的。若将剖析级别设置为2,剖析器将每次的读和写都记录到日志里: >...
  • mongodb查询分析

    2015-10-22 13:42:20
    0 摘要 在MySQL中,查询日志是经常作为我们优化查询的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是开启Profiling功能。该工具在运行的实例上收集有关MongoDB的写操作,游标,数据库命令等,可以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,061
精华内容 4,424
关键字:

mongodb慢日志分析