接业务需求,有一个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. React Context上下文

    目录 前言 一 context旧版使用步骤 1.1 根组件childContextTypes属性 1.2 根组件getChildContext方法 1.3 子组件contextTypes静态属性 1. ...

  2. CF 1130A 1130B 1130C1129A1 1129A2 1129B(Round542A B C D1 D2 E)题解

    A : Be Positive 题目地址:https://codeforces.com/problemset/problem/1130/A 题解:让你求是否满足一个d使得数列长为n的a数组的每个数除以 ...

  3. POJ 3281 Dining(网络流-拆点)

    Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will c ...

  4. Coderforces-455A

    Alex doesn't like boredom. That's why whenever he gets bored, he comes up with games. One long winte ...

  5. Java并发编程系列-(3) 原子操作与CAS

    3. 原子操作与CAS 3.1 原子操作 所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何context switch,也就是切换到另一个线程. 为了实 ...

  6. 面试题-关于Java线程池一篇文章就够了

    在Java面试中,线程池相关知识,虽不能说是必问提,但出现的频次也是非常高的.同时又鉴于公众号"程序新视界"的读者后台留言让写一篇关于Java线程池的文章,于是就有本篇内容,本篇将 ...

  7. Python3 并发编程2

    目录 进程互斥锁 基本概念 互斥锁的使用 IPC 基本概念 队列 生产者消费者模型 基本概念 代码实现 线程 基本概念 创建线程 线程互斥锁 进程互斥锁 基本概念 临界资源: 一次仅允许一个进程使用的 ...

  8. Linux(CentOS)安装SQL Server

    1.下载Microsoft SQL Server Red Hat 存储库配置文件 curl -o /etc/yum.repos.d/mssql-server.repo https://packages ...

  9. H5中被废弃的标签

    <br>换行,已经被<p>标签进行替换 <hr>画线 <font> <b>,<u>,<i>,<s>:加粗 ...

  10. eps-07s,编译及其烧写

    项目导入 清理并编译 会出现两个bin文件,然后烧写 修改红框中的东西,然后返回操作界面,进行一键烧写 硬件接线图 设备调试