返回顶部
首页 > 资讯 > 数据库 >MongoDB复合索引引发的灾难是怎样的
  • 209
分享到

MongoDB复合索引引发的灾难是怎样的

2024-04-02 19:04:59 209人浏览 八月长安
摘要

这期内容当中小编将会给大家带来有关mongoDB复合索引引发的灾难是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。前情提要11月末我司商品服务的MonGoDB主库曾

这期内容当中小编将会给大家带来有关mongoDB复合索引引发的灾难是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

前情提要

11月末我司商品服务的MonGoDB主库曾出现过严重抖动、频繁库等情况。

由于诸多业务存在插入MongoDB、然后立即查询等逻辑,因此项目并未开启读写分离。

最终定位问题是由于:服务器自身磁盘 + 大量慢查询导致

基于上述情况,运维同学后续着重增强了对MongoDB慢查询的监控和告警

幸运的一点:在出事故之前刚好完成了缓存过期时间的升级且过期时间为一个月,C端查询都落在缓存上,因此没有造成P0级事故,仅仅阻塞了部分B端逻辑

事故回放

我司的各种监控做的比较到位,当天突然收到了数据库服务器负载较高的告警通知,于是我和同事们就赶紧登录了Zabbix监控,如下图所示,截图的时候是正常状态,当时事故期间忘记留图了,可以想象当时的数据曲线反正是该高的很低,该低的很高就是了。

Zabbix 分布式监控系统官网:https://www.zabbix.com/

MongoDB复合索引引发的灾难是怎样的

开始分析

我们研发是没有操控服务器权限的,因此委托运维同学帮助我们抓取了部分查询记录,如下所示:

---------------------------------------------------------------------------------------------------------------------------+ Op          | Duration | Query                                                                                                                   ---------------------------------------------------------------------------------------------------------------------------+ query       | 5 s      | {"filter": {"orGCode": 350119, "fixedStatus": {"$in": [1, 2]}}, "sort": {"_id": -1}, "find": "sku_main"}                query       | 5 s      | {"filter": {"orgCode": 350119, "fixedStatus": {"$in": [1, 2]}}, "sort": {"_id": -1}, "find": "sku_main"}               query       | 4 s      | {"filter": {"orgCode": 346814, "fixedStatus": {"$in": [1, 2]}}, "sort": {"_id": -1}, "find": "sku_main"}               query       | 4 s      | {"filter": {"orgCode": 346814, "fixedStatus": {"$in": [1, 2]}}, "sort": {"_id": -1}, "find": "sku_main"}              query       | 4 s      | {"filter": {"orgCode": 346814, "fixedStatus": {"$in": [1, 2]}}, "sort": {"_id": -1}, "find": "sku_main"} ...

查询很慢的话所有研发应该第一时间想到的就是索引的使用问题,所以立即检查了一遍索引,如下所示:

### 当时的索引  db.sku_main.ensureIndex({"orgCode": 1, "_id": -1},{background:true}); db.sku_main.ensureIndex({"orgCode": 1, "upcCode": 1},{background:true}); ....

我屏蔽了干扰项,反正能很明显的看出来,这个查询是完全可以命中索引的,所以就需要直面第一个问题:

上述查询记录中排首位的慢查询到底是不是出问题的根源?

我的判断是:它应该不是数据库整体缓慢的根源,因为第一它的查询条件足够简单暴力,完全命中索引,在索引之上有一点其他的查询条件而已,第二在查询记录中也存在相同结构不同条件的查询,耗时非常短。

在运维同学继续排查查询日志时,发现了另一个比较惊爆的查询,如下:

### 当时场景日志  query: { $query: { shopCategories.0: { $exists: false }, orgCode: 337451, fixedStatus: { $in: [ 1, 2 ] }, _id: { $lt: 2038092587 } }, $orderby: { _id: -1 } } planSummary: IXSCAN { _id: 1 } ntoreturn:1000 ntoskip:0 keysExamined:37567133 docsExamined:37567133 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:293501 nreturned:659 reslen:2469894 locks:{ Global: { acquireCount: { r: 587004 } }, Database: { acquireCount: { r: 293502 } }, Collection: { acquireCount: { r: 293502 } } }   # 耗时 179530ms

# 耗时耗时180秒且基于查询的执行计划可以看出,它走的是_id_索引,进行了全表扫描,扫描的数据总量为:37567133,不慢才怪。

迅速解决

定位到问题后,没办法立即修改,第一要务是:止损

结合当时的时间也比较晚了,因此我们发了公告,禁止了上述查询的功能并短暂暂停了部分业务,,过了一会之后进行了主从切换,再去看Zabbix监控就一切安好了。

分析根源

我们回顾一下查询的语句和我们预期的索引,如下所示:

### 原始Query db.getCollection("sku_main").find({          "orgCode" : NumberLong(337451),          "fixedStatus" : {              "$in" : [                 1.0,                  2.0             ]         },          "shopCategories" : {              "$exists" : false         },          "_id" : {              "$lt" : NumberLong(2038092587)         }     } ).sort(     {          "_id" : -1.0     } ).skip(1000).limit(1000);  ### 期望的索引 db.sku_main.ensureIndex({"orgCode": 1, "_id": -1},{background:true});

乍一看,好像一切都很Nice啊,字段orgCode等值查询,字段_id按照创建索引的方向进行倒序排序,为啥会这么慢?

但是,关键的一点就在 $lt 上

知识点一:索引、方向及排序

在MongoDB中,排序操作可以通过从索引中按照索引的顺序获取文档的方式,来保证结果的有序性。

如果MongoDB的查询计划器没法从索引中得到排序顺序,那么它就需要在内存中对结果排序。

注意:不用索引的排序操作,会在内存超过32MB时终止,也就是说MongoDB只能支持32MB以内的非索引排序

知识点二:单列索引不在乎方向

无论是MongoDB还是Mysql都是用的树结构作为索引,如果排序方向和索引方向相反,只需要从另一头开始遍历即可,如下所示:

# 索引 db.records.createIndex({a:1});   # 查询 db.records.find().sort({a:-1});  # 索引为升序,但是我查询要按降序,我只需要从右端开始遍历即可满足需求,反之亦然 MIN 0 1 2 3 4 5 6 7 MAX

MongoDB的复合索引结构

官方介绍:MongoDB supports compound indexes, where a single index structure holds  references to multiple fields within a collection’s documents.

复合索引结构示意图如下所示:

MongoDB复合索引引发的灾难是怎样的

该索引刚好和我们讨论的是一样的,userid顺序,score倒序。

我们需要直面第二个问题:复合索引在使用时需不需要在乎方向?

假设两个查询条件:

# 查询 一 db.getCollection("records").find({    "userid" : "ca2" }).sort({"score" : -1.0});   # 查询 二 db.getCollection("records").find({    "userid" : "ca2" }).sort({"score" : 1.0});

上述的查询没有任何问题,因为受到score字段排序的影响,只是数据从左侧还是从右侧遍历的问题,那么下面的一个查询呢?

# 错误示范 db.getCollection("records").find({    "userid" : "ca2",   "score" : {      "$lt" : NumberLong(2038092587)   } }).sort({"score" : -1.0});

错误原因如下:

  • 由于score字段按照倒序排序,因此为了使用该索引,所以需要从左侧开始遍历

  • 从倒序顺序中找小于某个值的数据,势必会扫描很多无用数据,然后丢弃,当前场景下找大于某个值才是最佳方案

  • 所以MongoDB为了更多场景考虑,在该种情况下,放弃了复合索引,选用其他的索引,如 score 的单列索引

针对性修改

仔细阅读了根源之后,再回顾线上的查询语句,如下:

### 原始Query db.getCollection("sku_main").find({          "orgCode" : NumberLong(337451),          "fixedStatus" : {              "$in" : [                 1.0,                  2.0             ]         },          "shopCategories" : {              "$exists" : false         },          "_id" : {              "$lt" : NumberLong(2038092587)         }     } ).sort(     {          "_id" : -1.0     } ).skip(1000).limit(1000);  ### 期望的索引 db.sku_main.ensureIndex({"orgCode": 1, "_id": -1},{background:true});

犯的错误一模一样,所以MongoDB放弃了复合索引的使用,该为单列索引,因此进行针对性修改,把 $lt 条件改为 $gt 观察优化结果:

# 原始查询 [TEMP INDEX] => lt: {"limit":1000,"queryObject":{"_id":{"$lt":2039180008},"categoryId":23372,"orgCode":351414,"fixedStatus":{"$in":[1,2]}},"restrictedTypes":[],"skip":0,"sortObject":{"_id":-1}}  # 原始耗时 [TEMP LT] => 超时 (超时时间10s)  # 优化后查询 [TEMP INDEX] => gt: {"limit":1000,"queryObject":{"_id":{"$gt":2039180008},"categoryId":23372,"orgCode":351414,"fixedStatus":{"$in":[1,2]}},"restrictedTypes":[],"skip":0,"sortObject":{"_id":-1}}  # 优化后耗时 [TEMP GT] => 耗时: 383ms , List Size: 999

分析了小2000字,其实改动就是两个字符而已,当然真正的改动需要考虑业务的需要,但是问题既然已经定位,修改什么的就不难了,

上述就是小编为大家分享的MongoDB复合索引引发的灾难是怎样的了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网数据库频道。

您可能感兴趣的文档:

--结束END--

本文标题: MongoDB复合索引引发的灾难是怎样的

本文链接: https://lsjlt.com/news/61350.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • MongoDB复合索引引发的灾难是怎样的
    这期内容当中小编将会给大家带来有关MongoDB复合索引引发的灾难是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。前情提要11月末我司商品服务的MongoDB主库曾...
    99+
    2024-04-02
  • MongoDB中的索引是怎样的
    本篇文章给大家分享的是有关MongoDB中的索引是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。索引就像图书的目录一样,可以让我们快速定位到需要的内容,关系型数据库中有索...
    99+
    2023-06-19
  • 【MongoDB学习笔记21】MongoDB的复合索引
    索引的值是按照一定顺序排列的,因此使用索引键对文档进行搜索排序比较快;但是只有首先使用索引进行排序时,索引才有用;例如下面的排序里,“username”上的索引就没有起作用:> db.use...
    99+
    2024-04-02
  • mongodb中索引分类是怎样的以及如何创建索引
    mongodb中索引分类是怎样的以及如何创建索引,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。学习索引分类和创建索引:   &nbs...
    99+
    2024-04-02
  • mysql的联合索引(复合索引)的实现
    联合索引 本文中联合索引的定义为(MySQL): ALTER TABLE `table_name` ADD INDEX (`col1`,`col2`,`col3`); 联合索引的优点 若多个一条SQL,需要多个...
    99+
    2022-05-29
    mysql 联合索引 mysql 复合索引
  • 怎么查看mongodb集合的索引
    要查看MongoDB集合的索引,您可以使用以下两种方法:1. 使用MongoDB的索引管理命令:- 打开MongoDB shell,...
    99+
    2023-08-31
    mongodb
  • 由JavaScript的with 引发的探索是怎样的
    本篇文章给大家分享的是有关由JavaScript的with 引发的探索是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1. 背景某天吃饭的时候突然想到,都说 w...
    99+
    2023-06-26
  • MySQL怎样选择合适的索引
    这篇文章主要介绍了MySQL怎样选择合适的索引,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。先来看一个栗子EXPLAIN sele...
    99+
    2024-04-02
  • Oracle中复合索引与空值的索引怎么用
    这篇文章给大家分享的是有关Oracle中复合索引与空值的索引怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在QQ群里讨论一个SQL优化的问题,语句大致如下:selec...
    99+
    2024-04-02
  • 索引是 Python 面试中的难点,Laravel 框架又会怎样?
    索引是数据库中经常被提到的一个概念。它可以用来提高数据库的查询效率,也是Python面试中的一个难点。但是对于Laravel框架而言,它的索引实现有何不同呢?本文将为您详细介绍索引在Python和Laravel中的应用和实现。 一、Pyt...
    99+
    2023-10-31
    索引 面试 laravel
  • 关于mysql的索引是怎样的
    本篇文章给大家分享的是有关关于mysql的索引是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、mysql中的索引是什么?索引在mys...
    99+
    2024-04-02
  • MySQL的复合索引总结
    目录1.背景2.认识复合索引3.最左匹配原则4.字段顺序的影响5.单字段是否可以触发索引?7.复合索引可以替代单一索引吗?1.背景 最近频繁出现慢SQL导致系统性能...
    99+
    2024-04-02
  • 基于Java的全文索引引擎Lucene是怎样的
    今天给大家介绍一下基于Java的全文索引引擎Lucene是怎样的。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。基于Java的全文索引引擎LuceneLucen...
    99+
    2023-06-03
  • MongoDB一次节点宕机引发的思考是怎样的
    本篇文章为大家展示了MongoDB一次节点宕机引发的思考是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。简介最近一个 MongoDB 集群环境中的某节点异常下...
    99+
    2024-04-02
  • MySQL索引结构是怎么样的
    这篇文章主要为大家展示了“MySQL索引结构是怎么样的”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“MySQL索引结构是怎么样的”这篇文章吧。数据库存储单位首先...
    99+
    2024-04-02
  • MongoDB中二级索引和主索引的区别是什么
    MongoDB中的主索引是指集合中的_id字段,默认情况下_id字段会被自动创建并作为主索引。主索引是集合中的唯一索引,它保证了集合...
    99+
    2024-04-19
    MongoDB
  • MySQL中的聚簇索引、非聚簇索引、联合索引和唯一索引是什么
    今天小编给大家分享一下MySQL中的聚簇索引、非聚簇索引、联合索引和唯一索引是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章...
    99+
    2023-04-21
    mysql
  • MySQL中复合索引和覆盖索引的区别详解
    目录前言准备复合索引覆盖索引总结前言准备 我们先准备一张表和几个字段,方便介绍覆盖索引和复合索引。 创建一个user表,表中有id、name、school、age字段。 字段名字段类型idintnamevarcharsc...
    99+
    2023-11-23
    MySQL 复合索引 MySQL 覆盖索引
  • InnoDB表的索引有哪些特性,索引组织结构是怎样的?
      1、InnoDB聚集索引特点  我们知道,InnoDB引擎的聚集索引组织表,必然会有一个聚集索引。  行数据(row data)存储在聚集索引的叶子节点(除了发生overflow的列,参见 ,后面简称 ...
    99+
    2024-04-02
  • Mysql的复合索引如何生效
    目录背景认识复合索引最左匹配原则字段顺序的影响复合索引可以替代单一索引吗?小结背景 最近频繁出现慢SQL导致系统性能问题,于是决定针对索引进行一些优化。一些表结构本身已经有了不少索...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作