一个MongoDB索引走偏的案例及探究分析
接业务需求,有一个MongoDB的简单查询,太耗时了,执行了 70S 左右,严重影响用户的体验。。
查询代码主要如下:
db.duoduologmodel.find({"Tags.SN": "QZ435698245"})
.projection({})
.sort({OPTime: -})
.limit(20)
此集合在字段OPTime上有索引idx_OPTime;在"Tags"数组中的内嵌字段"SN"有索引idx_TSN;两者都是独立的索引。此集合存放的是执行Log,相对Size较大。
查看此查询对应的慢查询日志,如下:
--13T15::16.767+ I COMMAND [conn536359] command shqq_zp.duoduologmodel command: find { find: "duoduologmodel", filter: { Tags.SN: "QZ435698245" }, sort: { OPTime: - }, projection: {}, limit: , $db: "shqq_zp", $clusterTime: { clusterTime: Timestamp(, ), signature: { hash: BinData(, E7B0A887E83BAD0AA0A72016A39C677B53ABDBE2), keyId: } }, lsid: { id: UUID("0f22409b-f122-41e9-a094-46ccf04e44c7") } } planSummary: IXSCAN { OPTime: } cursorid: keysExamined: docsExamined: fromMultiPlanner: replanned: numYields: nreturned: reslen: locks:{ Global: { acquireCount: { r: } }, Database: { acquireCount: { r: } }, Collection: { acquireCount: { r: } } } protocol:op_msg 692537ms
查看此查询的执行计划,执行代码
db.duoduologmodel.find({"Tags.SN": "QZ435698245"})
.projection({})
.sort({OPTime: -})
.explain()
主要反馈信息
"queryPlanner" : {
"plannerVersion" : ,
"namespace" : "shqq_zp.duoduologmodel",
"indexFilterSet" : false,
"parsedQuery" : {
"Tags.SN" : {
"$eq" : "QZ435698245"
}
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"OPTime" : -
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Tags.SN" :
},
"indexName" : "idx_TSN",
"isMultiKey" : true,
"multiKeyPaths" : {
"Tags.SN" : [
"Tags"
]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : ,
"direction" : "forward",
"indexBounds" : {
"Tags.SN" : [
"[\"QZ435698245\", \"QZ435698245\"]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "FETCH",
"filter" : {
"Tags.SN" : {
"$eq" : "QZ435698245"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"OPTime" :
},
"indexName" : "idx_OPTime",
"isMultiKey" : false,
"multiKeyPaths" : {
"OPTime" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : ,
"direction" : "backward",
"indexBounds" : {
"OPTime" : [
"[MaxKey, MinKey]"
]
}
}
}
]
}
假如不用排序,删除 .sort({OperationTime: -1}),其执行计划
"queryPlanner" : {
"plannerVersion" : ,
"namespace" : "shqq_zp.duoduologmodel",
"indexFilterSet" : false,
"parsedQuery" : {
"Tags.SN" : {
"$eq" : "QZ435698245"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Tags.SN" :
},
"indexName" : "idx_TSN",
"isMultiKey" : true,
"multiKeyPaths" : {
"Tags.SN" : [
"Tags"
]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : ,
"direction" : "forward",
"indexBounds" : {
"Tags.SN" : [
"[\"QZ435698245\", \"QZ435698245\"]"
]
}
}
},
"rejectedPlans" : [ ]
}
此时,执行查询确实变快了很多,在2S以内执行完毕。
删除OPTime字段索引后的执行计划
"queryPlanner" : {
"plannerVersion" : ,
"namespace" : "shqq_zp.duoduologmodel",
"indexFilterSet" : false,
"parsedQuery" : {
"Tags.SN" : {
"$eq" : "QZ435698245"
}
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"OPTime" : -
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Tags.SN" :
},
"indexName" : "idx_TSN",
"isMultiKey" : true,
"multiKeyPaths" : {
"Tags.SN" : [
"Tags"
]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : ,
"direction" : "forward",
"indexBounds" : {
"Tags.SN" : [
"[\"QZ435698245\", \"QZ435698245\"]"
]
}
}
}
}
},
"rejectedPlans" : [ ]
}
删除这个索引后,查看报错
{
"message" : "Executor error during find command :: caused by :: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.",
"OPTime" : "Timestamp(1565681389, 1)",
"ok" : ,
"code" : ,
"codeName" : "OperationFailed",
"$clusterTime" : {
"clusterTime" : "Timestamp(1565681389, 1)",
"signature" : {
"hash" : "vODWe0BCzyihrRVM08wPSFIMvo0=",
"keyId" : ""
}
},
"name" : "MongoError"
}
原因比较明确:Sort operation used more than the maximum 33554432 bytes of RAM.,33554432 bytes算下来正好是32Mb,而Mongodb的sort操作是把数据拿到内存中再进行排序的,为了节约内存,默认给sort操作限制了最大内存为32Mb,当数据量越来越大直到超过32Mb的时候就自然抛出异常了!
因这个查看功能执行不多,并发不高。将系统排序内存由默认的32M调整到64M。
(此操作需谨慎,一般不建议修改,需要结合业务的使用情况,比如并发,数据量的大小;应优先考虑通过调整索引或集合的设计、甚至前端的设计来实现优化。)
db.adminCommand({setParameter:, internalQueryExecMaxBlockingSortBytes:})
(注意;这个设置在重启MongoDB服务就会失效,重新变成默认的32M了)。
再次执行查询查看,不再报错。并且快速返回结果(2S)
因为还有其他需求,会根据OPTime字段查看,重新添加这个索引。
再次执行,也可以比较迅速的返回结果(2S)。
从以上分析我们可以推断;
(1)explain()查看的执行计划,有时候还是有偏差的。
(2)Sort排序情况会影响索引的选择。即当internalQueryExecMaxBlockingSortBytes不足以支持先查询(by tag.sn)后排序(by optime)时,系统自动选择了一个已排序好的索引(by optime),进行查看。
知识补充:
queryPlanner.namespace:该值返回的是该query所查询的表;
queryPlanner.indexFilterSet:针对该query是否有indexfilter;
queryPlanner.winningPlan:查询优化器针对该query所返回的最优执行计划的详细内容;
queryPlanner.rejectedPlans:其他执行计划(非最优而被查询优化器reject的)的详细返回。
本文版权归作者所有,未经作者同意不得转载,谢谢配合!!!
本文版权归作者所有,未经作者同意不得转载,谢谢配合!!!
本文版权归作者所有,未经作者同意不得转载,谢谢配合!!!
一个MongoDB索引走偏的案例及探究分析的更多相关文章
- 【四】MongoDB索引管理
一.索引介绍 在mongodb中,索引用来支持高效查询.如果没有索引,mongodb必须在整个集合中扫描每个文档来查找匹配的文档.但是如果建立合适的索引,mongodb就可以通过索引来限制检查的文档数 ...
- Mongodb索引和执行计划 hint 慢查询
查询索引 索引存放在system.indexes集合中 > show tables address data person system.indexes 默认会为所有的ID建上索引 而且无法删除 ...
- MongoDB索引的简单理解
目录 MongoDB索引 1.语法准备 2.数据准备: 3.索引 3.1 唯一索引 3.2 单键索引 3.3 多键索引 3.4 复合索引 3.5 交叉索引 3.6 部分索引 3.7覆盖索引 3.8 全 ...
- [DataBase] MongoDB (7) MongoDB 索引
MongoDB 索引 1. 建立索引 唯一索引db.passport.ensureIndex( {"loginname": 1}, {"unique": tru ...
- MongoDB索引介绍
MongoDB中的索引其实类似于关系型数据库,都是为了提高查询和排序的效率的,并且实现原理也基本一致.由于集合中的键(字段)可以是普通数据类型,也可以是子文档.MongoDB可以在各种类型的键上创建索 ...
- MongoDB(索引及C#如何操作MongoDB)(转载)
MongoDB(索引及C如何操作MongoDB) 索引总概况 db.test.ensureIndex({"username":1})//创建索引 db.test.ensureInd ...
- BuguMongo是一个MongoDB Java开发框架,集成了DAO、Query、Lucene、GridFS等功能
http://code.google.com/p/bugumongo/ 简介 BuguMongo是一个MongoDB Java开发框架,它的主要功能包括: 基于注解的对象-文档映射(Object-Do ...
- MongoDB学习笔记(六) MongoDB索引用法和效率分析
MongoDB中的索引其实类似于关系型数据库,都是为了提高查询和排序的效率的,并且实现原理也基本一致.由于集合中的键(字段)可以是普通数据类型,也可以是子文档.MongoDB可以在各种类型的键上创建索 ...
- MongoDB索引(一)
原文地址 一.介绍 我们已经很清楚索引会提高查询效率.如果没有索引,MongoDB必须对全部集合进行扫描,即,扫描集合中每条文档以选择那些符合查询条件的文档.对查询来说如果存在合适的索引,则Mongo ...
随机推荐
- 基于 SOA 架构,创建 ego-search-web 项目-solr集群-zookeeper集群
项目架构 Ego-search-web 服务的消费者,ego-rpc 服务提供者 建立 ego-search-web 项目 继承:ego 依赖:ego-common ego-rpc-service ...
- [TimLinux] JavaScript 模态框可拖动功能实现——jQuery版
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 【Java SE】使用Javacv截取视频图片
[Java Web开发学习]使用Javacv截取视频图片 转载:https://www.cnblogs.com/yangchongxing/p/9482935.html github地址:https: ...
- 清晰架构(Clean Architecture)的Go微服务: 程序结构
我使用Go和gRPC创建了一个微服务,并试图找出最佳的程序结构,它可以用作我未来程序的模板. 我有Java背景,并发现自己在Java和Go之间挣扎,它们之间的编程理念完全不同.我写了一系列关于在项目工 ...
- NodeJS2-1环境&调试----CommonJS
CommonJS 每个文件是一个模块,有自己的作用域 在模块内部module变量代表模块本身 module.exports属性代表模块对外接口 require规则 /表示绝对路径,./表示型对于当前文 ...
- NodeJS1-2 Node JS的优点
NodeJS并不适合所有server端的场景,它的使用场景是及其有限的,web是nodeJS最适合的场景 前端偏爱node的原因其内核就是JavaScript 前端职责范围变大,统一开发体验 在处理了 ...
- Linux中的 date 使用
01. 日期格式字符串列表 %H 小时(以00-23来表示). %I 小时(以01-12来表示). %K 小时(以0-23来表示). %l 小时(以0-12来表示). %M 分钟(以00-59来表示) ...
- 从KafkaConsumer看看Kafka(一)
Kafka的消息模型为发布订阅模型,消息生产者将消息发布到主题(topic)中,一个或多个消费者订阅(消费)该主题消息并消费,此模型中发布到topic中的消息会被所有消费者所订阅到,先介绍Kafk ...
- 用js写九九乘法表格,附带背景颜色
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...
- 管程(Moniter): 并发编程的基本心法
JavaStorm 关注公众号获取更多并发 在吃透 Syncchronized 原理 中介绍了关于 Synchronize的实现原理,无论是同步方法还是同步代码块,无论是ACC_SYNCHRONIZE ...