接业务需求,有一个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. [TimLinux] asciinema Linux终端录制工具嵌入私有web中

    yum install asciinema https://github.com/asciinema/asciinema-player # 下载asciinema-player.css, asciin ...

  2. Koa - 初体验(写个接口)

    前言 不会node.js的前端不是一个好前端! 这几年node.js确实是越来越火了,好多公司对node.js都开始有要求.虽说前端不一定要会后端,但想要成为一个优秀的前端,node.js是必经之路. ...

  3. GitHub Actions 完成CI CD

    在之前我的部署.版本控制.CI.CD都是在Jenkins 下来完成的 在前几天看到github上的一个新玩具actions,简直惊为天人 它能在你的仓库触发事件(Push,Pull,issue,... ...

  4. 【算法】272-每周一练 之 数据结构与算法(Dictionary 和 HashTable)

    这是第五周的练习题,上周忘记发啦,这周是复习 Dictionary 和 HashTable. 下面是之前分享的链接: [算法]200-每周一练 之 数据结构与算法(Stack) [算法]213-每周一 ...

  5. 面试百度、阿里、腾讯,这134道Java面试题你会多少?

    这里一共是134道Java面试题,看看你能对几道吧! 1. Java 语言有哪些特点 2. 面向对象和面向过程的区别 3. 关于 JVM JDK 和 JRE 最详细通俗的解答 4. Oracle JD ...

  6. MySQL的安装、启动和基础配置 —— windows版本

    下载 第一步:打开网址,https://www.mysql.com,点击downloads之后跳转到https://www.mysql.com/downloads 第二步 :跳转至网址https:// ...

  7. Pycharm 解释器的快捷键

    Ctrl+shift+Z  反撤销 Ctrl +/ 注释 ctrl+d 复制粘贴选中 Ctrl+y 删除默认一行 Ctrl+shift+r 全局搜索 Ctrl+alt+/ 代码整理 compare w ...

  8. ajax数据交互

    目录 一.ORM查询优化 1-1. only与defer 1-2. select_related与prefatch_related 二.MTV与MVC模型 三.choices参数 四.AJAX 4-1 ...

  9. 微信Pcweb登录简介

    微信第三方PCweb扫码登录 准备工作 (1) 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统. 在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之 ...

  10. iOS 和 H5 页面交互(WKWebview 和 UIWebview cookie 设置)

    iOS 和 H5 页面交互(WKWebview 和 UIWebview cookie 设置) 主要记录关于cookie相关的坑 1. UIWebview 1. UIWebview 相对比较简单 直接通 ...