接业务需求,有一个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索引走偏的案例及探究分析的更多相关文章

  1. 【四】MongoDB索引管理

    一.索引介绍 在mongodb中,索引用来支持高效查询.如果没有索引,mongodb必须在整个集合中扫描每个文档来查找匹配的文档.但是如果建立合适的索引,mongodb就可以通过索引来限制检查的文档数 ...

  2. Mongodb索引和执行计划 hint 慢查询

    查询索引 索引存放在system.indexes集合中 > show tables address data person system.indexes 默认会为所有的ID建上索引 而且无法删除 ...

  3. MongoDB索引的简单理解

    目录 MongoDB索引 1.语法准备 2.数据准备: 3.索引 3.1 唯一索引 3.2 单键索引 3.3 多键索引 3.4 复合索引 3.5 交叉索引 3.6 部分索引 3.7覆盖索引 3.8 全 ...

  4. [DataBase] MongoDB (7) MongoDB 索引

    MongoDB 索引 1. 建立索引 唯一索引db.passport.ensureIndex( {"loginname": 1}, {"unique": tru ...

  5. MongoDB索引介绍

    MongoDB中的索引其实类似于关系型数据库,都是为了提高查询和排序的效率的,并且实现原理也基本一致.由于集合中的键(字段)可以是普通数据类型,也可以是子文档.MongoDB可以在各种类型的键上创建索 ...

  6. MongoDB(索引及C#如何操作MongoDB)(转载)

    MongoDB(索引及C如何操作MongoDB) 索引总概况 db.test.ensureIndex({"username":1})//创建索引 db.test.ensureInd ...

  7. BuguMongo是一个MongoDB Java开发框架,集成了DAO、Query、Lucene、GridFS等功能

    http://code.google.com/p/bugumongo/ 简介 BuguMongo是一个MongoDB Java开发框架,它的主要功能包括: 基于注解的对象-文档映射(Object-Do ...

  8. MongoDB学习笔记(六) MongoDB索引用法和效率分析

    MongoDB中的索引其实类似于关系型数据库,都是为了提高查询和排序的效率的,并且实现原理也基本一致.由于集合中的键(字段)可以是普通数据类型,也可以是子文档.MongoDB可以在各种类型的键上创建索 ...

  9. MongoDB索引(一)

    原文地址 一.介绍 我们已经很清楚索引会提高查询效率.如果没有索引,MongoDB必须对全部集合进行扫描,即,扫描集合中每条文档以选择那些符合查询条件的文档.对查询来说如果存在合适的索引,则Mongo ...

随机推荐

  1. 简单高效的端口扫描python脚本

    欢迎python爱好者加入:学习交流群 667279387 最近为了获取虚拟机端口开放情况,写了一个简单脚本来查看.共享给大家.下面的代码在python2种测试通过 说明:concurrent是pyt ...

  2. Geoserver2.15.1 配置自带 GeoWebCache 插件发布 ArcGIS Server 瓦片(附配置好的 Geoserver2.15.1 下载)

    之前写过一篇关于 Geoserver2.8.5 版本的部署配置发布 ArcGIS Server 瓦片点击查看,那是下载 Geoserver2.8.5 源码编译,重新打包 jar 来部署配置思路的,版本 ...

  3. NET视频教程分享

    地址:链接:https://pan.baidu.com/s/1q47WN1XFw19vLZ8XZqnB_g    提取码:8ut2  这是我收集的一套.NET学习视频教程(某智24期视频),分享出来, ...

  4. A.Changing Volume

    题目:改变音量 题意:给定两个数a和b,有6个操作(-5, -2, -1, +1, +2, +5),求a变到b的最小操作次数 操作的过程中不能变到小于0,即音量不能调到小于0 分析: (贪心),我们可 ...

  5. JS-变量、作用域、垃圾回收机制总结

    预解析时变量和函数同名的话,保留函数

  6. Koa中间件(middleware)级联原理

    前言 上次看到了koa-compose的代码,今天来说一下koa中间件的级联以及工作原理. 中间件工作原理 初始化koa实例后,我们会用use方法来加载中间件(middleware),会有一个数组来存 ...

  7. 【Maven】聚合

    [Maven]聚合 转载: 使用聚合一次能为多个 maven 项目执行命令,而不用到每一个项目下去执行命令. 聚合 pom 的特殊之处 1.packaging 配置 pom <packaging ...

  8. Node6-1单元测试mocha

    1.初步简单的测试 在src新建一个math.js的文件.src/math.js module.exports ={ add:(...args)=>{ return args.reduce((p ...

  9. 更改CSDN博客皮肤的一种简易方法

    CSDN改版后,皮肤设置变得不能够更改了,不过下面这种方法依然可以做到: 首先来到博客设置的主页面:. 接下来按ctrl + shift + i进入 如下页面,然后点击图中红色标记圈起来的选择元素按钮 ...

  10. SAP QM 检验批里样品数量的确定

    SAP QM 检验批里样品数量的确定 如下的检验批890000045939, 样品数量是50 PC. 检查该检验批对应的检验计划, 这些检验特性都有自己的取样策略,相关的取样数量,体现在结果录入界面, ...