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()
- 过期索引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 笔记03 查询、索引
查询 1. MongoDB使用find来进行查询.find的第一个参数决定了要返回哪些文档,这个参数是一个文档,用于指定查询条件.空的查询会匹配集合的全部内容.要是不指定查询,默认是{}. 2. 可以 ...
- MongoDB的第二天(更新,删除,查询,索引)
Mongodb的更新方式有三种 update函数,操作符更新,save函数 update: 语法格式:db.COLLECTION_NAME.update({查询条件},{更新内容},{更新参数(可选) ...
- MongoDB各种查询操作详解
这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下 一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...
- 【mongoDB中级篇②】索引与expain
索引的操作 数据库百分之八十的工作基本上都是查询,而索引能帮我们更快的查询到想要的数据.但是其降低了数据的写入速度,所以要权衡常用的查询字段,不必在太多字段上建立索引. 在mongoDB中默认是用bt ...
- MongoDB高级查询用法大全
转载 http://blog.163.com/lgh_2002/blog/static/440175262012052116455/ 详见官方的手册: http://www.mongodb.org/d ...
- MongoDB系列四(索引).
一.索引简介 再来老生常谈一番,什么是索引呢?数据库索引与书籍的索引类似.有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目以后,就可以直接跳转到目标文档的位置,这能使查找速度提高 ...
- MongoDb 命令查询所有数据库列表
原文:http://blog.csdn.net/huxu981598436/article/details/47216493 MongoDb 命令查询所有数据库列表 CODE: > show d ...
- MongoDB优化,建立索引实例及索引机制原理讲解
MongoDB优化,建立索引实例及索引机制原理讲解 为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样 ...
- Mongodb 笔记04 特殊索引和集合、聚合、应用程序设计
特殊索引和集合 1. 固定集合:固定集合需要事先创建好看,而且它的大小是固定的.当固定集合被占满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除. 2. 创建固定集合:db.createC ...
随机推荐
- 【前端干货】别再羡慕别人的Excel啦,教你点击按钮直接打开侧边栏!
负责技术支持的葡萄又来啦. 三日不见,我们的客户又为我们发来新的问题. 这次我们需要实现的场景是在前端表格环境中,像模板按钮那样,点击之后弹出一个侧边栏,然后通过点击不同的单元格显示不同的内容. 挤接 ...
- python中的sort用法
内置的列表类型提供sort的方法 可以根据多项指标给list实例中的元素排序.在默认情况下,sort方法总是按照自然升序排列列表内的元素 #升序排列 list1=[2,3,1,2,5] list1.s ...
- 面试官:Kafka是什么,它有什么特性与使用场景?
哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 不知不觉进入了五月份了,天气越 ...
- Java学习笔记-基础语法Ⅷ-泛型、Map
泛型 泛型本质上是参数化类型,也就是说所操作的数据类型被指定为一个参数,即将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型,这种参数类型可以用在类.方法和接口中,分别为泛型类.泛型方法 ...
- 使用Spring MVC开发RESTful API
第3章 使用Spring MVC开发RESTful API Restful简介 第一印象 左侧是传统写法,右侧是RESTful写法 用url描述资源,而不是行为 用http方法描述行为,使用http状 ...
- Linux内网渗透
Linux虽然没有域环境,但是当我们拿到一台Linux 系统权限,难道只进行一下提权,捕获一下敏感信息就结束了吗?显然不只是这样的.本片文章将从拿到一个Linux shell开始,介绍Linux内网渗 ...
- Random 中的Seed
C#中使用随机数 看下例 当Random的种子是0时 生成的随机数列表是一样的 也就是说当seed 一样时 审查的随机数时一样的 Random的无参实例默认 种子 时当前时间 如果要确保生成的随机数不 ...
- Linux Cgroup v1(中文翻译)(2):CPUSETS
英文原文:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpusets.html Copyright (C) 2004 BU ...
- 物联网无线数传应用中的Modbus通信网关协议到底是什么?
什么是物联网 通信Modbus网关 Modbus协议无线通信网关就是将一种Modbus协议帧转换为其他物联网无线数传协议帧. 比如将Modbus RTU的数据与Modbus TCP数据进行相互转换:也 ...
- 一款超级好用的3Dmax模型插件 支持模型多格式批量转换
对于模型设计师来说模型格式转换是最常见的事,但是每一款建模软件所支持的格式各有不同,模型互导操作太麻烦 为了解决这个难题,老子云平台研发了一款基于3dmax软件的模型格式转换插件,支持多种模型格式想换 ...