MongoDB慢查询

慢查询分析

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

示例:

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

MongoDB索引

什么是索引?

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

索引结构

MongoDB的索引结构为B树

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

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

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

索引分类

  1. 单键索引

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

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

示例:

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

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

db.user.getIndexes()
  1. 过期索引TTL

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

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

示例:

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

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

  1. 复合索引

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

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

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

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

示例:

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

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

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

针对地理空间坐标数据创建索引。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
}
])

索引管理

  1. 创建索引并在后台运行

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

db.COLLECTION_NAME.createIndex({"字段":排序方式}, {background: true});
  1. 查询某个集合的索引
db.COLLECTION_NAME.getIndexes()
  1. 查看索引大小
db.COLLECTION_NAME.totalIndexSize()
  1. 索引重建
db.COLLECTION_NAME.reIndex()
  1. 索引删除
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用来获取所有执行计划,结果参数基本与上文相同
  1. 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服务器信息
  1. 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慢查询与索引的更多相关文章

  1. Mongodb 笔记03 查询、索引

    查询 1. MongoDB使用find来进行查询.find的第一个参数决定了要返回哪些文档,这个参数是一个文档,用于指定查询条件.空的查询会匹配集合的全部内容.要是不指定查询,默认是{}. 2. 可以 ...

  2. MongoDB的第二天(更新,删除,查询,索引)

    Mongodb的更新方式有三种 update函数,操作符更新,save函数 update: 语法格式:db.COLLECTION_NAME.update({查询条件},{更新内容},{更新参数(可选) ...

  3. MongoDB各种查询操作详解

    这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下   一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...

  4. 【mongoDB中级篇②】索引与expain

    索引的操作 数据库百分之八十的工作基本上都是查询,而索引能帮我们更快的查询到想要的数据.但是其降低了数据的写入速度,所以要权衡常用的查询字段,不必在太多字段上建立索引. 在mongoDB中默认是用bt ...

  5. MongoDB高级查询用法大全

    转载 http://blog.163.com/lgh_2002/blog/static/440175262012052116455/ 详见官方的手册: http://www.mongodb.org/d ...

  6. MongoDB系列四(索引).

    一.索引简介 再来老生常谈一番,什么是索引呢?数据库索引与书籍的索引类似.有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目以后,就可以直接跳转到目标文档的位置,这能使查找速度提高 ...

  7. MongoDb 命令查询所有数据库列表

    原文:http://blog.csdn.net/huxu981598436/article/details/47216493 MongoDb 命令查询所有数据库列表 CODE: > show d ...

  8. MongoDB优化,建立索引实例及索引机制原理讲解

    MongoDB优化,建立索引实例及索引机制原理讲解 为什么需要索引? 当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样 ...

  9. Mongodb 笔记04 特殊索引和集合、聚合、应用程序设计

    特殊索引和集合 1. 固定集合:固定集合需要事先创建好看,而且它的大小是固定的.当固定集合被占满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除. 2. 创建固定集合:db.createC ...

随机推荐

  1. 解析数仓OLAP函数:ROLLUP、CUBE、GROUPING SETS

    摘要:GaussDB(DWS) ROLLUP,CUBE,GROUPING SETS等OLAP函数的原理解析. 本文分享自华为云社区<GaussDB(DWS) OLAP函数浅析>,作者: D ...

  2. vue3中的四种插槽的介绍-保证让你看看的明明白白!

    插槽 当组件中只有一个插槽的时候,我们可以不设置 slot 的 name 属性. v-slot 后可以不带参数,但是 v-slot 在没有设置 name 属性的时候, 插槽口会默认为"def ...

  3. '@JvmDefault' is only supported since JVM target 1.8. Recompile with '-jvm-ta

    Java 1.8加入了接口默认实现的方式. 那么在kotlin中如何使用呢? 可以在方法上加注解@JvmDefault 但直接加上后会编译会碰到一些错误提示.下面是解决方法. 问题 Logcat 提示 ...

  4. 关于 MyBatis-Plus 分页查询的探讨 → count 都为 0 了,为什么还要查询记录?

    开心一刻 记得上初中,中午午休的时候,我和哥们躲在厕所里吸烟 听见外面有人进来,哥们猛吸一口,就把烟甩了 进来的是教导主任,问:你们干嘛呢? 哥们鼻孔里一边冒着白烟一边说:我在生气 环境搭建 依赖引入 ...

  5. Solon 1.7.6 发布,更现代感的应用开发框架

    相对于 Spring Boot 和 Spring Cloud 的项目 启动快 5 - 10 倍 qps 高 2- 3 倍 运行时内存节省 1/3 ~ 1/2 打包可以缩小到 1/2 ~ 1/10(比如 ...

  6. Nginx代理websocket为什么要这样做?

    Nginx反向代理websocket 示例: http { map $http_upgrade $connection_upgrade { default upgrade; '' close; } s ...

  7. 防火墙之ipset表应用

    简介 由于公司线上生产环境firewalld默认都是关闭的,所以只能依赖iptables的表来方便管理IP黑白名单. 提示线上生产环境不要随便重启iptables,会导致docker已经映射好的关系表 ...

  8. Mybatis-Plus乐观锁Version

    实现原理 取出记录时,获取当前version更新时,带上这个version执行更新时, set version = newVersion where version = oldVersion如果ver ...

  9. 145_Power BI Report Server自定义Form登录

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 很久没有更新Power BI Report Server了,发现自己机器还是2021年1月版本的,现在更新了20 ...

  10. 亿信BI——EXCEL组件使用流程

    功能模块: 用户点击"excel输入"模块进行excel文件导入操作,excel导入页面的顶部导航栏包括基本属性.文件设置.格式设置和字段列表四部分. 基础属性: 在基本属性模块部 ...