MongoDB慢查询与索引实例详解

目录
  • MongoDB慢查询
  • MongoDB索引
  • 总结

MongoDB慢查询

慢查询分析

  • 开启内置的慢查询分析器
db.setProfilingLevel(n,m),n的取值可选0,1,2
  • 0:表示不记录
  • 1:表示记录慢速操作,如果值为1,m需要传慢查询的阈值,单位为ms
  • 2:表示记录所有的读写操作

示例:

db.setProfilingLevel(1,3)
  • 查询监控结果
db.system.profile.find().sort({millis:-1}).limit(3)

MongoDB索引

什么是索引?

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引目标是提高数据库的查询效率,没有索引的话,查询会进行全表扫描(scaneverydocumentinacollection),数据量大时严重降低了查询效率。默认情况下Mongo在一个集合(collection)创建时,自动地对集合的_id创建了唯一索引。

索引结构

MongoDB的索引结构为B树

B树非叶子节点也存了数据,查询效率不固定,最好的情况是O(1),在单次查询的情况下平均性能是优于B+树的。而MongoDB是被作为一个单一查询比较多,遍历数据比较少的一个定位。所以采用了B树。

那为什么不用单次性能更好的Hash结构呢?

因为虽然遍历数据的情况较少,但是对于遍历数据也需要有相对较好的性能支持。Hash这种性能表现较为极端的数据结构往往只能在简单、极端的场景下使用。

索引分类

  • 单键索引

MongoDB支持所有数据类型中的单个字段索引,并且可以在文档的任何字段定义。对于单个字段索引,索引键的排序顺序无关紧要,因为MongoDB可以在任一方向读取索引。

db.集合名.createIndex({"字段名":排序方式})

示例:

db.user.createIndex({"name":1})

创建后可以通过查询索引命令查看是否创建成功。

db.user.getIndexes()
  • 唯一索引

设置unique为true。示例:

db.book.createIndex({title:1},{unique:true})
  • 过期索引TTL

TTL索引是MongoDB中一种特殊的索引,可以支持文档在一定时间之后自动过期删除,目前TTL索引只能在单字段上建立,并且字段类型必须是日期类型。

db.集合名.createIndex({"日期字段":排序方式}, {expireAfterSeconds: 秒数})

示例:

db.user.createIndex({"bithday":1}, {expireAfterSeconds: 10})

创建过期索引后,有bithday字段的文档会在约10秒后自动删除。

  • 复合索引

通常我们需要在多个字段上进行搜索,如果是这种情况,可以考虑使用复合索引。复合索引支持基于多个字段的索引,这扩展了索引的概念并将它们扩展到索引中的更大域。

建立复合索引需要注意:字段顺序和索引方向。它也是遵循最左前缀原则。

db.集合名.createIndex( { "字段名1" : 排序方式, "字段名2" : 排序方式 } )
  • 多键索引

针对属性包含数组数据的情况,MongoDB支持针对数组中每一个element创建索引,支持Strings、numbers、nested documents。

示例:

//type是集合类型的数据,创建的就是多键索引
db.book.insert({title:"java",type:["技术","IT"]})

db.book.createIndex({type:1})
  • 哈希索引

针对属性的哈希值进行索引查询,当要使用Hashed Index时,MongoDB能够自动计算hash值来进行查询。

db.集合.createIndex({"字段": "hashed"})
  • 地理空间索引

针对地理空间坐标数据创建索引。2dsphere索引:用于存储和查找球面上的点。

2d索引:用于存储和查找平面上的点。

db.集合名.ensureIndex({字段名:"2dsphere"})

示例:

//插入数据
db.company.insert({
    loc:{type:"Point",coordinates:[116.482451,39.914176]},
    name:"大望路",
    category:"Parks"
})

//创建索引
db.company.ensureIndex({loc:"2dsphere"})

//查询范围内的数据
db.company.find({
    "loc":{
    "$geoWithin":{
        "$center":[[116.482450,39.914176],0.05]
    }
}
})

//距离指定位置最近的2个点
db.company.aggregate([
    {
        $geoNear: {
          near: {
              type: "Point",
              coordinates: [ 116.472451,39.814176]
          },
          key:"loc",
          distanceField: "dist.calculated",
          spherical: true
        }
    },
    {
        $limit: 2
    }
    ])

索引管理

  • 创建索引并在后台运行

有时数据量大的时候,创建索引的动作是比较耗费时间的,这时后台运行就比较有用了。

db.COLLECTION_NAME.createIndex({"字段":排序方式}, {background: true});
  • 查询某个集合的索引
db.COLLECTION_NAME.getIndexes()
  • 查看索引大小
db.COLLECTION_NAME.totalIndexSize()
  • 索引重建
db.COLLECTION_NAME.reIndex()
  • 索引删除
db.COLLECTION_NAME.dropIndex("INDEX-NAME")
db.COLLECTION_NAME.dropIndexes()
注意: _id 对应的索引是删除不了的

Explain分析

explain()是一个查询分析的方法,它还可以接收不同的参数来查看更详细的查询计划。

简单示例:

db.user.find().explain()
db.user.find({name:"test1"}).explain("executionStats")

参数介绍:

  • queryPlanner:queryPlanner是默认参数,具体执行计划信息参考下面的表格
  • executionStats:executionStats会返回执行计划的一些统计信息(有些版本中和allPlansExecution等同)。
  • allPlansExecution:allPlansExecution用来获取所有执行计划,结果参数基本与上文相同
  • queryPlanner参数查询返回值含义
参数 含义
plannerVersion 查询计划版本
namespace 要查询的集合(该值返回的是该query所查询的表)数据库.集合
indexFilterSet 针对该query是否有indexFilter
parsedQuery 查询条件
winningPlan 被选中的执行计划
winningPlan.stage 被选中执行计划的stage(查询方式),常见的有:COLLSCAN/全表扫描:(应该知道就是CollectionScan,就是所谓的“集合扫描”,和mysql中tablescan/heapscan类似,这个就是所谓的性能最烂最无奈的由来)、IXSCAN/索引扫描:(是IndexScan,这就说明我们已经命中索引了)、FETCH/根据索引去检索文档、SHARD_MERGE/合并分片结果、IDHACK/针对_id进行查询等
winningPlan.inputStage 用来描述子stage,并且为其父stage提供文档和索引关键字。
winningPlan.stage的child stage 如果此处是IXSCAN,表示进行的是index scanning。
winningPlan.keyPattern 所扫描的index内容
winningPlan.indexName winning plan所选用的index。
winningPlan.isMultiKey 是否是Multikey,此处返回是false,如果索引建立在array上,此处将是true。
winningPlan.direction 此query的查询顺序,此处是forward,如果用了.sort({字段:-1})将显示backward。
filter 过滤条件
winningPlan.indexBounds winningplan所扫描的索引范围,如果没有制定范围就是[MaxKey,MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。
rejectedPlans 被拒绝的执行计划的详细返回,其中具体信息与winningPlan的返回中意义相同,故不在此赘述
serverInfo MongoDB服务器信息
  • executionStats参数查询返回值含义
参数 含义
executionSuccess 是否执行成功
nReturned 返回的文档数
executionTimeMillis 执行耗时
totalKeysExamined 索引扫描次数
totalDocsExamined 文档扫描次数
executionStages 这个分类下描述执行的状态
stage 扫描方式,具体可选值与上文的相同
nReturned 查询结果数量
executionTimeMillisEstimate 检索document获得数据的时间
inputStage.executionTimeMillisEstimate 该查询扫描文档 index所用时间
works 工作单元数,一个查询会分解成小的工作单元
advanced 优先返回的结果数
docsExamined 文档检查数目,与totalDocsExamined一致。检查了总共的document个数,而从返回上面的nReturned数量

这么多返回值我们怎么分析呢?

首先我们先造点数据:

for(var i=0;i<100000;i++){
    db.user.insert({
        name:"test"+i,
        explectSalary:10+i
    })
}

查询耗时115

db.user.find({name:'test1'}).explain("allPlansExecution")

然后创建索引

db.user.createIndex({name:1})

再次查询,查看耗时变为了2。速度直线飙升。我们再对返回结果做一个分析:

{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.user",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "name" : {
                                "$eq" : "test1"
                        }
                },
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "name" : 1
                                },
                                "indexName" : "name_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "name" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "name" : [
                                                "[\"test1\", \"test1\"]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 2,
                "executionTimeMillis" : 2,
                "totalKeysExamined" : 2,
                "totalDocsExamined" : 2,
                "executionStages" : {
                        "stage" : "FETCH",
                        "nReturned" : 2,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 3,
                        "advanced" : 2,
                        "needTime" : 0,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "docsExamined" : 2,
                        "alreadyHasObj" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 2,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 3,
                                "advanced" : 2,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "keyPattern" : {
                                        "name" : 1
                                },
                                "indexName" : "name_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "name" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "name" : [
                                                "[\"test1\", \"test1\"]"
                                        ]
                                },
                                "keysExamined" : 2,
                                "seeks" : 1,
                                "dupsTested" : 0,
                                "dupsDropped" : 0
                        }
                },
                "allPlansExecution" : [ ]
        },
        "serverInfo" : {
                "host" : "10.0.3.15",
                "port" : 27017,
                "version" : "4.2.21",
                "gitVersion" : "b0aeed9445ff41af07449fa757e1f231bce990b3"
        },
        "ok" : 1
}

折叠

重要参数介绍:

  • executionStats.executionTimeMillis 整体查询时间
  • executionStats.executionStages.executionTimeMillisEstimate 该查询检索document获得数据的时间
  • executionStats.inputStage.executionTimeMillisEstimate 该查询扫描文档index所用的时间
  • executionStats.nReturned 查询返回的条数
  • executionStats.totalKeysExamined:索引扫描条数
  • executionStats.totalDocsExamined:文档扫描条数

对于一个查询,我们最理想的状态是:nReturned=totalKeysExamined=totalDocsExamined

  • stage状态:它的值有很多,如下所示:

类型列举如下:

  • COLLSCAN:全表扫描
  • IXSCAN:索引扫描
  • FETCH:根据索引去检索指定document
  • SHARD_MERGE:将各个分片返回数据进行merge
  • SORT:表明在内存中进行了排序
  • LIMIT:使用limit限制返回数
  • SKIP:使用skip进行跳过
  • IDHACK:针对_id进行查询
  • SHARDING_FILTER:通过mongos对分片数据进行查询
  • COUNT:利用db.coll.explain().count()之类进行count运算
  • TEXT:使用全文索引进行查询时候的stage返回
  • PROJECTION:限定返回字段时候stage的返回

还有的是上面的组合

  • Fetch+IDHACK
  • Fetch+IXSCAN
  • Limit+(Fetch+IXSCAN)
  • PROJECTION+IXSCAN
  • SHARDING_FITER+IXSCAN

总结 

到此这篇关于MongoDB慢查询与索引的文章就介绍到这了,更多相关MongoDB慢查询与索引内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解MongoDB的复合索引

    为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样适用于其他的数据库比如mysql). mongo-9552:PRIMARY> db.person.find() { "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" :

  • MongoDB中创建索引需要注意的事项

    上周在 ruby-china 上发了帖子<MongoDB 那些坑>,反映相当热烈,许多回复很有见地,其中一位童鞋深入的提到 MongoDB 建索引方法的问题,引发我更深入的了解了 MongoDB 建索引的方法和一些注意事项. 在 <MongoDB 那些坑>中提到,在前台直接运行建立索引命令的话,将造成整个数据库阻塞,因此索引建议使用 background 的方式建立.但是这也会带来一定的问题,在 2.6 版本之前,在 secondary server 中即使使用 backgroun

  • MongoDB使用profile分析慢查询的步骤

    在MongoDB中,如果发生了慢查询,我们如何得到这些慢查询的语句,并优化呢?今天来看这块儿的一些心得. 01 如何收集慢查询? 在MongoDB中,通常可以开启profile来收集慢日志,查看当前profile状态的语句如下: test1:PRIMARY> db.getProfilingStatus() {         "was" : 2,         "slowms" : 0,         "sampleRate" : 1,

  • MongoDB索引使用详解

    索引就像书的目录,如果查找某内容在没有目录的帮助下,只能全篇查找翻阅,这导致效率非常的低下:如果在借助目录情况下,就能很快的定位具体内容所在区域,效率会直线提高. 索引简介 首先打开命令行,输入mongo.默认mongodb会连接名为test的数据库. ➜ ~ mongo MongoDB shell version: 2.4.9 connecting to: test > show collections > 可以使用show collections/tables查看数据库为空. 然后在mon

  • MongoDB慢查询与索引实例详解

    目录 MongoDB慢查询 MongoDB索引 总结 MongoDB慢查询 慢查询分析 开启内置的慢查询分析器 db.setProfilingLevel(n,m),n的取值可选0,1,2 0:表示不记录 1:表示记录慢速操作,如果值为1,m需要传慢查询的阈值,单位为ms 2:表示记录所有的读写操作 示例: db.setProfilingLevel(1,3) 查询监控结果 db.system.profile.find().sort({millis:-1}).limit(3) MongoDB索引 什

  • MongoDB 查询操作的实例详解

    MongoDB 查询操作的实例详解 使用find或findOne进行查询.并可以进行范围查询.数据集查询.不等式查询,以及其他的一些查询. 查询将会返回DBcursor 游标只有在你需要的时候返回文档 针对游标返回的文档(结果集) 进行操作 例如:忽略一定数量的结果,或者返回结果的数量,以及对结果的排序. 1.指定需要返回的键 有时候仅仅对文档的某几个键值感兴趣,可以屏蔽返回的不感兴趣的键值,返回感兴趣的键值 mongos> db.blog.find({},{"name":1})

  • php mysql PDO 查询操作的实例详解

    php mysql PDO 查询操作的实例详解 <?php $dbh = new PDO('mysql:host=localhost;dbname=access_control', 'root', ''); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $dbh->exec('set names utf8'); /*添加*/ //$sql = "INSERT INTO `user` SET `log

  • Vue.js进行查询操作的实例详解

    Vue.js进行查询操作的实例详解 实例代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script src="../lib/vue.min.js" type="text/javascript" ></script> <title>字符转换</title> </head>

  • springboot Mongodb的集成与使用实例详解

    说说springboot与大叔lind.ddd的渊源 Mongodb在Lind.DDD中被二次封装过(大叔的.net和.net core),将它当成是一种仓储来使用,对于开发人员来说只公开curd几个标准的接口即可,而在springboot框架里,它与大叔lind有些类似之处,同样是被二次封装了,开发人员只需要关注自己的业务即可,而标准的curd操作完成由springboot帮助我们来实现,一般地,我们会设计一个与实体对象的接口仓储,让它去继承mongo的标准接口,然后在springboot的依

  • C#根据IP地址查询所属地区实例详解

    ip-api.com接口(解析 json需要引入Newtonsoft.Json.dll ): /// <summary> /// 根据IP 获取物理地址 /// </summary> /// <param name="ip">Ip地址</param> /// <returns></returns> public static string GetIpAddress(string ip) { string url =

  • MongoDB中的定时索引示例详解

    MongoDB中存在一种索引,叫做TTL索引(time-to-live index,具有生命周期的索引),这种索引允许为每一个文档设置一个超时时间.一个文档达到预设置的老化程度后就会被删除. 数据到期对于某些类型的信息非常有用,例如机器生成的事件数据,日志和会话信息,这些信息只需要在数据库中保存有限的时间. 在createIndex中指定expireAfterSeconds选项就可以创建一个TTL索引: // 超时时间为24小时,默认是前台运行,可以通过background:true设置为后台模

  • C#使用有道ip地址查询接口方法实例详解

    本文实例讲述了C#使用有道ip地址查询接口方法.分享给大家供大家参考.具体实现方法如下: #region 读取http://www.yodao.com接口IP地址 /// <summary> /// 读取http://www.yodao.com接口IP地址 /// </summary> public static string GetstringIpAddress(string strIP)//strIP为IP { string sURL = "http://www.yo

  • Python3.5 Pandas模块缺失值处理和层次索引实例详解

    本文实例讲述了Python3.5 Pandas模块缺失值处理和层次索引.分享给大家供大家参考,具体如下: 1.pandas缺失值处理 import numpy as np import pandas as pd from pandas import Series,DataFrame df3 = DataFrame([ ["Tom",np.nan,456.67,"M"], ["Merry",34,345.56,np.nan], [np.nan,np

  • Go语言ORM包中使用worm构造查询条件的实例详解

    目录 构造查询条件 main函数 数据库表与数据模型 通过ID来查询数据 通过Where函数来查询数据 XXXIf查询 in.not in查询 嵌套查询语句 Limit与Offset orderby查询 构造查询条件 worm是一款方便易用的Go语言ORM库.worm支Model方式(持结构体字段映射).原生SQL以及SQLBuilder三种模式来操作数据库,并且Model方式.原生SQL以及SQLBuilder可混合使用. Model方式.SQL builder支持链式API,可使用Where

随机推荐