zoukankan      html  css  js  c++  java
  • 监控Mongo慢查询

    监控Mongo慢查询

    1. 使用mongostat监控MongoDB全局情况

     mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取MongoDB的当前运行状态,并输出。
     如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。
     
     mongostat --host localhost:27017 -uroot -p123456 --authenticationDatabase admin
     参数说明:
     host:指定IP地址和端口,也可以只写IP,然后使用--port参数指定端口号
     -u: 如果开启了认证,则需要在其后填写用户名
     -p:  密码
     --authenticationDatabase:若开启了认证,则需要在此参数后填写认证库(注意是认证上述账号的数据库)
    

    mongostat输出详解

    insert/s : 官方解释是每秒插入数据库的对象数量,如果是slave,则数值前有*,则表示复制集操作
    query/s : 每秒的查询操作次数
    update/s : 每秒的更新操作次数
    delete/s : 每秒的删除操作次数
    getmore/s: 每秒查询cursor(游标)时的getmore操作数
    command: 每秒执行的命令数,在主从系统中会显示两个值(例如 3|0),分表代表 本地|复制 命令
    注: 一秒内执行的命令数比如批量插入,只认为是一条命令(所以意义应该不大)
    dirty: 仅仅针对WiredTiger引擎,官网解释是脏数据字节的缓存百分比
    used:仅仅针对WiredTiger引擎,官网解释是正在使用中的缓存百分比
    flushes:
    For WiredTiger引擎:指checkpoint的触发次数在一个轮询间隔期间
    For MMAPv1 引擎:每秒执行fsync将数据写入硬盘的次数
    注:一般都是0,间断性会是1, 通过计算两个1之间的间隔时间,可以大致了解多长时间flush一次。flush开销是很大的,
        如果频繁的flush,可能就要找找原因了
    vsize: 虚拟内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)
    res:  物理内存使用量,单位MB (这是 在mongostat 最后一次调用的总数据)
    注:这个和你用top看到的一样, vsize一般不会有大的变动, res会慢慢的上升,如果res经常突然下降,去查查是否有别的程序狂吃内存。
    
    qr: 客户端等待从MongoDB实例读数据的队列长度
    qw: 客户端等待从MongoDB实例写入数据的队列长度
    ar: 执行读操作的活跃客户端数量
    aw: 执行写操作的活客户端数量
    注:如果这两个数值很大,那么就是DB被堵住了,DB的处理速度不及请求速度。看看是否有开销很大的慢查询。如果查询一切正常,确实是负载很大,就需要加机器了
    netIn:MongoDB实例的网络进流量
    netOut:MongoDB实例的网络出流量
    注:此两项字段表名网络带宽压力,一般情况下,不会成为瓶颈
    conn: 打开连接的总数,是qr,qw,ar,aw的总和
    注:MongoDB为每一个连接创建一个线程,线程的创建与释放也会有开销,所以尽量要适当配置连接数的启动参数,
       maxIncomingConnections,阿里工程师建议在5000以下,基本满足多数场景
    set: 副本集的名称
    repl: 节点的复制状态
     M     ---master
     SEC     ---secondary
     REC     ---recovering
     UNK     ---unknown
     SLV     ---slave
     RTR     ---mongs process("router')
     ARB     ---arbiter
    

    2. 使用Profiling捕捉慢查询

    类似于MySQL的slow log, mongodb可以监控所有慢的以及不慢的查询。这个工具就是Profiling,该工具在运行的实例上收集有关MongoDB的 写操作,游标,数据库命令等,可以在数据库级别开启该工具,也可以在实例级别开启。该工具会把收集到的所有都写入到system.profile集合中,该集合是一个capped collection。Profiling功能肯定是会影响效率的,但是不太严重,原因是他使用的是system.profile 来记录,而system.profile 是一个capped collection, 这种collection 在操作上有一些限制和特点,但是效率更高。

    2.1 慢查询分析过程

    1. 设置一个时间阀值,比如200ms
    2. 在profiling中(system.profile)找到超过200ms的语句
    3. 查看execStats,分析执行计划
    4. 根据分析结果,决定是不是需要添加索引
    

    2.2 Profiling基本操作

    mongoshell(或者其他客户端比如mongochef等
    #查看状态:级别和时间
    PRIMARY> db.getProfilingStatus()
    { "was" : 1, "slowms" : 200 }

    #查看级别
    PRIMARY> db.getProfilingLevel()
    
    #级别说明:
    0:关闭,不收集任何数据。
    1:收集慢查询数据,默认是100毫秒。
    2:收集所有数据
    
    #设置级别
    PRIMARY> db.setProfilingLevel(2)
    { "was" : 1, "slowms" : 100, "ok" : 1 }  #这里返回的是上一次的设置
    
    #设置级别和时间
    PRIMARY> db.setProfilingLevel(1,200)
    { "was" : 2, "slowms" : 100, "ok" : 1 }  #这里返回的是上一次的设置
    
    #关闭Profiling
    PRIMARY> db.setProfilingLevel(0)
    { "was" : 1, "slowms" : 200, "ok" : 1 }  #这里返回的是上一次的设置
    
    
    #清空system.profile或者修改大小
    #关闭Profiling
    PRIMARY> db.setProfilingLevel(0)
    { "was" : 0, "slowms" : 200, "ok" : 1 }
    #删除system.profile集合
    PRIMARY> db.system.profile.drop()
    true
    #创建一个新的system.profile集合 --- 4M
    PRIMARY> db.createCollection( "system.profile", { capped: true, size:4000000 } )
    { "ok" : 1 }
    #重新开启Profiling
    PRIMARY> db.setProfilingLevel(1,200)
    { "was" : 0, "slowms" : 200, "ok" : 1 }
    
    #如果是复制集环境,要修改副本的system.profile的大小,必须把副本先从复制集中剔除,然后执行上述步骤,最后加入复制集。
    
    #也可以MongoDB启动时,开启Profiling   
    mongod --profile=1  --slowms=200
    #或者在配置文件里添加
    profile = 1
    slowms = 200
    

    3. 日常使用的Profiling查询脚本

    #返回最近的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()
    #从一个特定的时间范围内返回信息
    db.system.profile.find(
                      {
                       ts : {
                             $gt : new ISODate("2015-10-18T03:00:00Z"),
                             $lt : new ISODate("2015-10-19T03:40:00Z")
                            }
                      }
                     ).pretty()
    #特定时间,限制用户,按照消耗时间排序
    db.system.profile.find(
                      {
                        ts : {
                              $gt : newISODate("2015-10-12T03:00:00Z") ,
                              $lt : newISODate("2015-10-12T03:40:00Z")
                             }
                      },
                      { user : 0 }
                     ).sort( { millis : -1 } )
    #查看最新的 Profile  记录: 
    db.system.profile.find().sort({$natural:-1}).limit(1)
    # 显示5个最近的事件
    show profile
    

    4. 案例分析

    4.1 获取慢查询

    #下面的语句过滤几个大表,因为基本无法优化,需要开发改逻辑,所以做了排除,在输出方面只输出了个人认为重要的,方便分析迅速定位
    

    db.system.profile.find({"ns":{"$not":{"$in":["F10data3.f10_4_4_1_gsgg_content", "F10data3.f10_5_1_1_gsyb_content"]}}}, {"ns":1,"op":1, "query":1,"keysExamined":1,"docsExamined":1,"numYield":1, "planSummary":1,"responseLength":1,"millis":1,"execStats":1}).limit(10).sort({ts:-1}).pretty()

    #下面是一个超过200ms的查询语句
    { 
    "op" : "query",                     #操作类型,有insert、query、update、remove、getmore、command  
    "ns" : "F10data3.f10_2_8_3_jgcc", 
    "query" : {                         #具体的查询语句 包括过滤条件,limit行数  排序字段
        filter" : {
            "jzrq" : {
                "$gte" : ISODate("2017-03-31T16:00:00.000+0000"), 
                "$lte" : ISODate("2017-06-30T15:59:59.000+0000")
            }, 
            "jglxfldm" : 10.0
        }, 
        "ntoreturn" : 200.0,    
        "sort" : {                      #如果有排序  则显示排序的字段 这里是 RsId
            "RsId" : 1.0
        }
    }, 
    "keysExamined" : 0.0,               #索引扫描数量 这里是全表扫描,没有用索引 所以是 0
    "docsExamined" : 69608.0,           #浏览的文档数 这里是全表扫描 所以是整个collection中的全部文档数
    "numYield" : 546.0,                 #该操作为了使其他操作完成而放弃的次数。通常来说,当他们需要访问
                                        还没有完全读入内存中的数据时,操作将放弃。这使得在MongoDB为了
                                        放弃操作进行数据读取的同时,还有数据在内存中的其他操作可以完成。
    "locks" : {                         #锁信息,R:全局读锁;W:全局写锁;r:特定数据库的读锁;w:特定数据库的写锁
        "Global" : {
            "acquireCount" : {
                "r" : NumberLong(1094)  #该操作获取一个全局级锁花费的时间。
            }
        }, 
        "Database" : {
            "acquireCount" : {
                "r" : NumberLong(547)  
            }
        }, 
        "Collection" : {
            "acquireCount" : {
                "r" : NumberLong(547)
            }
        }
    }, 
    "nreturned" : 200.0,                #返回的文档数量
    "responseLength" : 57695.0,         #返回字节长度,如果这个数字很大,考虑值返回所需字段
    "millis" : 264.0,                   #消耗的时间(毫秒)
    "planSummary" : "COLLSCAN, COLLSCAN", #执行概览 从这里看来 是全表扫描 
    "execStats" : {                       #详细的执行计划 这里先略过 后续可以用 explain来具体分析
    }, 
    "ts" : ISODate("2017-08-24T02:32:49.768+0000"),  #命令执行的时间
    "client" : "10.3.131.96",                        #访问的ip或者主机
    "allUsers" : [
    
    ], 
    "user" : ""
    }
    

    4.2 分析慢查询

     1. 如果发现 millis 值比较大,那么就需要作优化。
     2. 如果docsExamined数很大,或者接近记录总数(文档数),那么可能没有用到索引查询,而是全表扫描。
     3. 如果keysExamined数为0,也可能是没用索引。
     4. 结合 planSummary 中的显示,上例中是  "COLLSCAN, COLLSCAN" 确认是全表扫描
     5. 如果 keysExamined 值高于 nreturned 的值,说明数据库为了找到目标文档扫描了很多文档。这时可以考虑创建索引来提高效率。
     6. 索引的键值选择可以根据 query 中的输出参考,上例中 filter:包含了 jzrq和jglxfldm 并且按照RsId排序,所以 我们的索引
        索引可以这么建: db.f10_2_8_3_jgcc.ensureindex({jzrq:1,jglxfldm:1,RsId:1})
    

    4.3 执行计划中的TYPE类型

    COLLSCAN     #全表扫描                                                避免
    IXSCAN       #索引扫描                                                可以改进 选用更高效的索引
    FETCH        #根据索引去检索指定document                               
    SHARD_MERGE  #将各个分片返回数据进行merge                               尽可能避免跨分片查询
    SORT         #表明在内存中进行了排序(与老版本的scanAndOrder:true一致)   排序要有index  
    LIMIT        #使用limit限制返回数                                      要有限制 Limit+(Fetch+ixscan)最优
    SKIP         #使用skip进行跳过                                         避免不合理的skip
    IDHACK       #针对_id进行查询                                          推荐,_id 默认主键,查询速度快
    SHARDING_FILTER  #通过mongos对分片数据进行查询                          SHARDING_FILTER+ixscan最优 
    COUNT        #利用db.coll.explain().count()之类进行count运算             
    COUNTSCAN    #count不使用Index进行count时的stage返回                    避免 这种情况建议加索引
    COUNT_SCAN   #count使用了Index进行count时的stage返回                    推荐
    SUBPLA       #未使用到索引的$or查询的stage返回                           避免
    TEXT         #使用全文索引进行查询时候的stage返回  
    PROJECTION   #限定返回字段时候stage的返回                                选择需要的数据, 推荐PROJECTION+ixscan
  • 相关阅读:
    Python:判断文本中的用户名在数据库中是否存在,存在返回1,不存在返回0
    Mongodb创建修改用户权限
    web开发一些常用的工具类的网站
    python linux交互模块(paramiko、fabric与pexpect)
    python内建模块详解
    python笔记_01_打开文件读取一行的最好方法
    轻量级RPC设计与实现第二版
    轻量级RPC设计与实现第一版
    Dubbo-服务注册中心之AbstractRegistry
    dubbo之心跳机制
  • 原文地址:https://www.cnblogs.com/chinesern/p/7427967.html
Copyright © 2011-2022 走看看