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 ...
随机推荐
- UART串口及Linux实现
UART,全称Universal Asynchronous Receiver Transmitter,通用异步收发器,俗称串口.作为最常用的通信接口之一,从8位单片机到64位SoC,一般都会提供UAR ...
- 基于C++11的线程池实现
1.线程池 1.1 线程池是什么? 一种线程管理方式. 1.2 为什么用线程池? 线程的创建和销毁都需要消耗系统开销,当线程数量过多,系统开销过大,就会影响缓存局部性和整体性能.而线程池能够在充分利用 ...
- gitee上传代码到仓库步骤(最简洁图文+命令说明)
gitee上传代码到仓库步骤(最简洁图文+命令说明) 目录 1.初始化项目 2.将当前目录下所有需要上传的文件代码等资源添加到缓存区 3. 提交缓存区里面的主要内容到本地仓库 4.添加一个远程仓库的地 ...
- Resource wordnet not found. Please use the NLTK Downloader to obtain the resource:
第一次使用nltk的时候,出现了这样的错误: from nltk.stem.wordnet import WordNetLemmatizer lemmatizer = WordNetLemmatize ...
- 什么叫做 Docker
什么叫做 Docker 本文写于 2020 年 11 月 5 日 没有人会喜欢环境配置 在去年的时候我开始学习 Python,并利用 Python 制作了一些小工具.但问题是我很难让别人去用我的软件, ...
- 关于position的relative和absolute分别是相对于谁进行定位的
position:absolute; 他的意思是绝对定位,他是参照浏览器的左上角,配合TOP.RIGHT.BOTTOM.LEFT(下面简称TRBL)进行定位,在没有设定TRBL,默认依据父级的做标原始 ...
- 274. H-Index - LeetCode
Question 274. H-Index Solution 题目大意: 论文里的 h 因子判定,题目的意思可能有点晦涩.h 因子是评判学术成就的一种重要方法,h 因子越高越好,h 因子兼顾研究学术人 ...
- 干掉Switch-Case、If-Else----订阅发布模式+事件驱动模式
在上位机和下位机或者服务端和客户端通信的时候,很多时候可能为了赶项目进度或者写代码方便直接使用Socket通信,传输string类型的关键字驱动对应的事件,这就有可能导致程序中存在大量的Switch- ...
- 一个恢复CSI挂载信息的解决方法
一个恢复CSI挂载信息的解决方法 问题描述 之前有做过一个华为OBS 的CSI插件,其基本运作原理如下图所示.CSI插件Pod挂载了主机的/var/lib/kubelet/pods目录,当创建挂载Pv ...
- Docker容器Nginx负载均衡配置、check及stub模块安装
Nginx是一款高性能的HTTP和反向代理.负载均衡web服务器.本次在Docker容器中部署三个tomcat,Nginx代理三个tomcat服务(以下称节点)来模拟实现负载均衡效果,配置check模 ...