Mongo低版本 count操作进行$in时走了覆盖索引却仍然回表

文章目录

    • 概要
    • 一、原因
    • 二、解决方法

概要

由于历史原因,一些老项目还在用MongoDB V3.2版本,集群为分片模式,一个客户表数量有15亿左右,昨天监控突然报很多慢查询,如下:

db.info.count({domain_id:888,status:{$in:[2,3]}})

表结构关键字段如下:

字段类型注释
domain_idint64公司id
customer_idint64公司下的客户id
statusint32客户状态

索引如下:

db.info.createIndex({domain_id:1,status:1,modify_time:1})

一、原因

遇到慢查询,当然要explain下看下查询语句执行状况了,如下:

db.info.explain('executionStats').count({domain_id:4730422,status:{$in:[2,3]}})
{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "customer.info","indexFilterSet" : false,"parsedQuery" : {"$and" : [{"domain_id" : {"$eq" : 4730422}},{"status" : {"$in" : [2,3]}}]},"winningPlan" : {"stage" : "COUNT","inputStage" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN","keyPattern" : {"domain_id" : 1,"status" : 1,"modify_time" : -1},"indexName" : "domain_id_1_status_1_modify_time_-1","isMultiKey" : false,"direction" : "forward","indexBounds" : {"domain_id" : ["[4730422.0, 4730422.0]"],"status" : ["[2.0, 2.0]","[3.0, 3.0]"],"modify_time" : ["[MaxKey, MinKey]"]}}}},"rejectedPlans" : []},"executionStats" : {"executionSuccess" : true,"nReturned" : 0,"executionTimeMillis" : 0,"totalKeysExamined" : 9,   #索引扫描个数"totalDocsExamined" : 9,   #文档扫描个数,不等于0表示有回表"executionStages" : {"stage" : "COUNT","nReturned" : 0,"executionTimeMillisEstimate" : 0,"works" : 10,"advanced" : 0,"needTime" : 9,"needFetch" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"invalidates" : 0,"nCounted" : 9,"nSkipped" : 0,"inputStage" : {"stage" : "FETCH","nReturned" : 9,"executionTimeMillisEstimate" : 0,"works" : 10,"advanced" : 9,"needTime" : 0,"needFetch" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"invalidates" : 0,"docsExamined" : 9,"alreadyHasObj" : 0,"inputStage" : {"stage" : "IXSCAN","nReturned" : 9,"executionTimeMillisEstimate" : 0,"works" : 10,"advanced" : 9,"needTime" : 0,"needFetch" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"invalidates" : 0,"keyPattern" : {"domain_id" : 1,"status" : 1,"modify_time" : -1},"indexName" : "domain_id_1_status_1_modify_time_-1","isMultiKey" : false,"direction" : "forward","indexBounds" : {"domain_id" : ["[4730422.0, 4730422.0]"],"status" : ["[2.0, 2.0]","[3.0, 3.0]"],"modify_time" : ["[MaxKey, MinKey]"]},"keysExamined" : 9,"dupsTested" : 0,"dupsDropped" : 0,"seenInvalidated" : 0,"matchTested" : 0}}} },"ok" : 1
}

通过上面索引执行分析可以看到 totalDocsExamined不为0,所以进行了回表,理论上走了覆盖索引是不用回表的。
我们下面再试试没有$in的情况下,查询分析:

db.info.explain('executionStats').count({domain_id:4730422,status:3})
{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "customer.info","indexFilterSet" : false,"parsedQuery" : {"$and" : [{"domain_id" : {"$eq" : 4730422}},{"status" : {"$eq" : 3}}]},"winningPlan" : {"stage" : "COUNT","inputStage" : {"stage" : "COUNT_SCAN","keyPattern" : {"domain_id" : 1,"status" : 1,"modify_time" : -1},"indexName" : "domain_id_1_status_1_modify_time_-1","isMultiKey" : false}},"rejectedPlans" : [ ]},"executionStats" : {"executionSuccess" : true,"nReturned" : 0,"executionTimeMillis" : 0,"totalKeysExamined" : 8,"totalDocsExamined" : 0,"executionStages" : {"stage" : "COUNT","nReturned" : 0,"executionTimeMillisEstimate" : 0,"works" : 8,"advanced" : 0,"needTime" : 7,"needFetch" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"invalidates" : 0,"nCounted" : 6,"nSkipped" : 0,"inputStage" : {"stage" : "COUNT_SCAN","nReturned" : 6,"executionTimeMillisEstimate" : 0,"works" : 7,"advanced" : 6,"needTime" : 1,"needFetch" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"invalidates" : 0,"keysExamined" : 8,"keyPattern" : {"domain_id" : 1,"status" : 1,"modify_time" : -1},"indexName" : "domain_id_1_status_1_modify_time_-1","isMultiKey" : false}},"allPlansExecution" : [ ]},"ok" : 1
}

可以看到此时totalDocsExamined的值为0,说明不使用$in操作就不会回表了。这显然是count操作在$in时的bug。
我们用docker快速搭建一个MongoDB V4.4的副本集试试,如下:

rsm:PRIMARY> db.info.explain('executionStats').count({domain_id:15600,status:{$in:[2,3]}})
{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "customer.info","indexFilterSet" : false,"parsedQuery" : {"$and" : [{"domain_id" : {"$eq" : 15600}},{"status" : {"$in" : [2,3]}}]},"winningPlan" : {"stage" : "COUNT","inputStage" : {"stage" : "IXSCAN","keyPattern" : {"domain_id" : 1,"status" : 1},"indexName" : "domain_id_1_status_1","isMultiKey" : false,"multiKeyPaths" : {"domain_id" : [ ],"status" : [ ]},"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {"domain_id" : ["[15600.0, 15600.0]"],"status" : ["[2.0, 2.0]","[3.0, 3.0]"]}}},"rejectedPlans" : [ ]},"executionStats" : {"executionSuccess" : true,"nReturned" : 0,"executionTimeMillis" : 0,"totalKeysExamined" : 5,"totalDocsExamined" : 0, "executionStages" : {"stage" : "COUNT","nReturned" : 0,"executionTimeMillisEstimate" : 0,"works" : 6,"advanced" : 0,"needTime" : 5,"needYield" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"nCounted" : 5,"nSkipped" : 0,"inputStage" : {"stage" : "IXSCAN","nReturned" : 5,"executionTimeMillisEstimate" : 0,"works" : 6,"advanced" : 5,"needTime" : 0,"needYield" : 0,"saveState" : 0,"restoreState" : 0,"isEOF" : 1,"keyPattern" : {"domain_id" : 1,"status" : 1},"indexName" : "domain_id_1_status_1","isMultiKey" : false,"multiKeyPaths" : {"domain_id" : [ ],"status" : [ ]},"isUnique" : false,"isSparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {"domain_id" : ["[15600.0, 15600.0]"],"status" : ["[2.0, 2.0]","[3.0, 3.0]"]},"keysExamined" : 5,"seeks" : 1,"dupsTested" : 0,"dupsDropped" : 0}}},"serverInfo" : {"host" : "b8b178041a4e","port" : 27021,"version" : "4.4.28","gitVersion" : "61c2baf63a060f7c12bd76e779044800ae18710b"},"ok" : 1,"$clusterTime" : {"clusterTime" : Timestamp(1711857956, 1),"signature" : {"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),"keyId" : NumberLong(0)}},"operationTime" : Timestamp(1711857956, 1)
}

可以看到此时totalDocsExamined的值为0,说明至少MongoDB V4.4以后该bug修复了。

二、解决方法

1:升级MongoDB版本
2:既然不使用$in就可以,那就count两次,程序中累加起来。

由此可以看出,有条件的话,项目的基础组件及时升级也是很有必要的,新的版本肯定是修复了以往bug、做了性能优化以及功能新增的,好处多多。否则项目必然会慢慢腐化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/782985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

梦中梦中梦?(梦中梦?)

梦中梦中梦?(梦中梦?) 早上7.40左右起床上厕所(大的)开始自律的一天,上完了刷了会手机,决定再睡一会。在起和睡之间犹豫了几次还是决定睡一会,就开始了这辈子头一次梦中…

算法打卡day22

今日任务: 1)216.组合总和III 2)17.电话号码的字母组合 216.组合总和III 题目链接:216. 组合总和 III - 力扣(LeetCode) 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数&#xf…

配置内网pip源

PIP源(内网) 配置windows配置: pip config set global.index-url http://192.168.102.7:8080/pypi_tsinghua/simple/ pip config set global.trusted-host"192.168.102.7" Linux配置: 编辑pip默认源 mkdir ~/.pip vim ~/.pip/pip.conf 修改内容如…

9.2-源码分析:Dubbo Remoting 层 Buffer 缓冲区

Buffer 是一种字节容器,在 Netty 等 NIO 框架中都有类似的设计,例如,Java NIO 中的ByteBuffer、Netty4 中的 ByteBuf。Dubbo 抽象出了 ChannelBuffer 接口对底层 NIO 框架中的 Buffer 设计进行统一,其子类如下图所示: …

Oracle数据库——子查询五

14.1子查询语法 子查询 (内查询) 在主查询之前一次执行完成。子查询的结果被主查询(外查询)使用 。范例一:谁的工资比 Abel 高? 第一:查询Abel的工资是多少。第二:比较大于这个工资的人数。 注意事项: 子查询要包含在括号内。将子查询放在比较条件的右侧。</

Java集合的基础知识

目录 Java集合的基础知识 JavaArrayList成员方法 Java集合的练习 01-添加字符串并遍历 02-添加数字并遍历 03-添加学生对象并遍历1 04-添加学生对象并遍历2 Java集合的基础知识 JavaArrayList成员方法 在Java中&#xff0c;ArrayList 是一个可调整大小的数组实现&#…

文件名目录名或卷标语法不正确:数据恢复策略与预防措施

一、文件名目录名或卷标语法不正确的现象 在日常使用电脑或移动设备时&#xff0c;我们经常会遇到“文件名目录名或卷标语法不正确”的错误提示。这种错误通常发生在尝试访问、修改或删除文件、目录或卷标时&#xff0c;系统会提示无法完成操作&#xff0c;因为文件名、目录名…

C++格式化输入和输出

格式化输入与输出 除了条件状态外&#xff0c;每个iostream对象还维护一个格式状态来控制IO如何格式化的细节。 格式状态控制格式化的某些方面&#xff0c;如整型值是几进制、浮点值的精度、一个输出元素的宽度等。 标准库定义了一组操纵符来修改流的格式状态。 一个操纵符…

电商系列之商详

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

java解数独(力扣Leetcode37)

数独问题 力扣原题链接 问题描述 数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 数独部分空格内已填入了数字&#xff0c;空白格用.表示。 示例 示例&…

微服务监控:确保分布式系统的可观察性与稳定性

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、前言二、微服务监控的重要性三、关键监控指标四、常用监控工具五、最佳实践六、结论 一、前言 在当前的软件开发领域&a…

const在指针中的作用以及*p在各种写法中分别代表什么含义

const在指针中起固定的作用&#xff0c;在不同的写法中其效果也有所区别&#xff0c;具体如下&#xff1a; 1、int* const p固定的是指针p指向的地址。 2、int const *p固定的是指针p指向地址中储存的内容。 例&#xff1a; 以上操作在编译器中执行不了&#xff0c;会报错。…

项目中预览图片时,添加水印,浏览器禁止右键功能、前端禁止直接获取图片地址。(Vue3、TS、canvas)

在src/utils文件夹下新建watermark.ts&#xff0c;写入以下代码块&#xff0c;生成水印文件 // 导出函数 getWatermark&#xff0c;它返回一个对象&#xff0c;其中包含一个名为 watermark 的方法。 export const getWatermark () > {const setWatermark (str: any) >…

Java基础知识总结(29)

Java虚拟机 运行时数据区 程序计数器 方法区&#xff1a;Java 8以后没有方法区&#xff0c;改为了元空间&#xff08;MetaSpace&#xff09; 虚拟机栈 堆 本地方法栈 程序计数器 它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里&#xff0c…

flutter生成二维码并截图保存到图库

引入库&#xff1a;flutter_screenutil、image_gallery_saver、qr_flutter弹窗布局 import dart:async; import dart:typed_data; import package/generated/l10n.dart; import package:jade/configs/PathConfig.dart; import package:jade/utils/ImageWaterMarkUtil.dart; im…

瑞吉外卖实战学习--8、人员禁用和启用

前言 1、通过前端页面查看接口 会发现请求方式是put 请求接口是employee 2、检查页面传值 根据浏览器的请求可以看到传值为id和status 2、写put请求&#xff0c;添加修改时间和修改人的id然后传回给后台 /*** 启用和禁用员工账号* param request* param employee* return…

【Consul】Linux安装Consul保姆级教程

【Consul】Linux安装Consul保姆级教程 大家好 我是寸铁&#x1f44a; 总结了一篇【Consul】Linux安装Consul保姆级教程✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天要把编写的go程序放到linux上进行测试Consul服务注册与发现&#xff0c;那怎么样才能实现这一过程&am…

25Ramdisk 启动模式简介

Ramdisk 启动模式简介 ramdisk是一种虚拟磁盘技术,我们的PE系统几乎都是使用ramdisk方式从计算机启动的.那么,ramdisk有哪些特点呢? Ramdisk 将内存虚拟为一个磁盘 Ramdisk技术会将你的一部分内存虚拟成一块磁盘分区.使用U盘启动pe系统时,打开pe系统里的文件资源管理器,你会看…

CKS之安全沙箱运行容器:gVisor

目录 一、gVisor介绍 二、gVisor架构 三、gVisor使用前置条件 四、Docker中使用gVisor 五、containerd中使用gVisor 六、Kubernetes结合gVisor使用 一、gVisor介绍 gVisor是Google开源的一种容器沙箱技术&#xff0c;其设计初衷是在提供较高安全性的同时&#xff0c;尽量…

Stable Diffusion 推荐硬件配置和本地化布署

Stable Diffusion简介 Stable Diffusion是由Stability AI开发的一种强大的文本到图像(Text-to-Image)生成模型,它能够根据用户提供的文本描述,生成与之相关的高质量、高分辨率图像。下面我从原理、特点、应用三个方面对Stable Diffusion作简要介绍: 1、原理:Stable Diffusion…