返回顶部
首页 > 资讯 > 数据库 >MongoDB中如何使用复合索引
  • 751
分享到

MongoDB中如何使用复合索引

2024-04-02 19:04:59 751人浏览 独家记忆
摘要

本篇文章给大家分享的是有关mongoDB中如何使用复合索引,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是复合索引?复合索引,即Compo

本篇文章给大家分享的是有关mongoDB中如何使用复合索引,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

什么是复合索引?

复合索引,即Compound Index,指的是将多个键组合到一起创建索引,这样可以加速匹配多个键的查询。不妨通过一个简单的示例理解复合索引。

students集合如下:

db.students.find().pretty()
{
 "_id" : ObjectId("5aa7390ca5be7272a99b042a"),
 "name" : "zhang",
 "age" : "15"
}
{
 "_id" : ObjectId("5aa7393ba5be7272a99b042b"),
 "name" : "wang",
 "age" : "15"
}
{
 "_id" : ObjectId("5aa7393ba5be7272a99b042c"),
 "name" : "zhang",
 "age" : "14"
}

在name和age两个键分别创建了索引(_id自带索引):

db.students.getIndexes()
[
 {
 "v" : 1,
 "key" : {
 "name" : 1
 },
 "name" : "name_1",
 "ns" : "test.students"
 },
 {
 "v" : 1,
 "key" : {
 "age" : 1
 },
 "name" : "age_1",
 "ns" : "test.students"
 }
]

当进行多键查询时,可以通过explian()分析执行情况(结果仅保留winningPlan):

db.students.find({name:"zhang",age:"14"}).explain()
"winningPlan":
{
 "stage": "FETCH",
 "filter":
 {
  "name":
  {
   "$eq": "zhang"
  }
 },
 "inputStage":
 {
  "stage": "IXSCAN",
  "keyPattern":
  {
   "age": 1
  },
  "indexName": "age_1",
  "isMultiKey": false,
  "isUnique": false,
  "isSparse": false,
  "isPartial": false,
  "indexVersion": 1,
  "direction": "forward",
  "indexBounds":
  {
   "age": [
    "[\"14\", \"14\"]"
   ]
  }
 }
}

由winningPlan可知,这个查询依次分为IXSCAN和FETCH两个阶段。IXSCAN即索引扫描,使用的是age索引;FETCH即根据索引去查询文档,查询的时候需要使用name进行过滤。

为name和age创建复合索引:

db.students.createIndex({name:1,age:1})
db.students.getIndexes()
[
 {
 "v" : 1,
 "key" : {
 "name" : 1,
 "age" : 1
 },
 "name" : "name_1_age_1",
 "ns" : "test.students"
 }
]

有了复合索引之后,同一个查询的执行方式就不同了:

db.students.find({name:"zhang",age:"14"}).explain()
"winningPlan":
{
 "stage": "FETCH",
 "inputStage":
 {
  "stage": "IXSCAN",
  "keyPattern":
  {
   "name": 1,
   "age": 1
  },
  "indexName": "name_1_age_1",
  "isMultiKey": false,
  "isUnique": false,
  "isSparse": false,
  "isPartial": false,
  "indexVersion": 1,
  "direction": "forward",
  "indexBounds":
  {
   "name": [
    "[\"zhang\", \"zhang\"]"
   ],
   "age": [
    "[\"14\", \"14\"]"
   ]
  }
 }
}

由winningPlan可知,这个查询的顺序没有变化,依次分为IXSCAN和FETCH两个阶段。但是,IXSCAN使用的是name与age的复合索引;FETCH即根据索引去查询文档,不需要过滤。

这个示例的数据量太小,并不能看出什么问题。但是实际上,当数据量很大,IXSCAN返回的索引比较多时,FETCH时进行过滤将非常耗时。接下来将介绍一个真实的案例。

定位MongoDB性能问题

随着接收的错误数据不断增加,我们Fundebug已经累计处理3.5亿错误事件,这给我们的服务不断带来性能方面的挑战,尤其对于MonGoDB集群来说。

对于生产数据库,配置profile,可以记录MongoDB的性能数据。执行以下命令,则所有超过1s的数据库读写操作都会被记录下来。

db.setProfilingLevel(1,1000)

查询profile所记录的数据,会发现events集合的某个查询非常慢:

db.system.profile.find().pretty()
{
 "op" : "command",
 "ns" : "fundebug.events",
 "command" : {
 "count" : "events",
 "query" : {
 "createAt" : {
 "$lt" : ISODate("2018-02-05T20:30:00.073Z")
 },
 "projectId" : ObjectId("58211791ea2640000c7a3fe6")
 }
 },
 "keyUpdates" : 0,
 "writeConflicts" : 0,
 "numYield" : 1414,
 "locks" : {
 "Global" : {
 "acquireCount" : {
 "r" : NumberLong(2830)
 }
 },
 "Database" : {
 "acquireCount" : {
 "r" : NumberLong(1415)
 }
 },
 "Collection" : {
 "acquireCount" : {
 "r" : NumberLong(1415)
 }
 }
 },
 "responseLength" : 62,
 "protocol" : "op_query",
 "millis" : 28521,
 "execStats" : {
 },
 "ts" : ISODate("2018-03-07T20:30:59.440Z"),
 "client" : "192.168.59.226",
 "allUsers" : [ ],
 "user" : ""
}

events集合中有数亿个文档,因此count操作比较慢也不算太意外。根据profile数据,这个查询耗时28.5s,时间长得有点离谱。另外,numYield高达1414,这应该就是操作如此之慢的直接原因。根据MongoDB文档,numYield的含义是这样的:

The number of times the operation yielded to allow other operations to complete. Typically, operations yield when they need access to data that MongoDB has not yet fully read into memory. This allows other operations that have data in memory to complete while MongoDB reads in data for the yielding operation.

这就意味着大量时间消耗在读取硬盘上,且读了非常多次。可以推测,应该是索引的问题导致的。

不妨使用explian()来分析一下这个查询(仅保留executionStats):

db.events.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})
"executionStats":
{
 "executionSuccess": true,
 "nReturned": 20853,
 "executionTimeMillis": 28055,
 "totalKeysExamined": 28338,
 "totalDocsExamined": 28338,
 "executionStages":
 {
  "stage": "FETCH",
  "filter":
  {
   "createAt":
   {
    "$lt": ISODate("2018-02-05T20:30:00.073Z")
   }
  },
  "nReturned": 20853,
  "executionTimeMillisEstimate": 27815,
  "works": 28339,
  "advanced": 20853,
  "needTime": 7485,
  "needYield": 0,
  "saveState": 1387,
  "restoreState": 1387,
  "isEOF": 1,
  "invalidates": 0,
  "docsExamined": 28338,
  "alreadyHasObj": 0,
  "inputStage":
  {
   "stage": "IXSCAN",
   "nReturned": 28338,
   "executionTimeMillisEstimate": 30,
   "works": 28339,
   "advanced": 28338,
   "needTime": 0,
   "needYield": 0,
   "saveState": 1387,
   "restoreState": 1387,
   "isEOF": 1,
   "invalidates": 0,
   "keyPattern":
   {
    "projectId": 1
   },
   "indexName": "projectId_1",
   "isMultiKey": false,
   "isUnique": false,
   "isSparse": false,
   "isPartial": false,
   "indexVersion": 1,
   "direction": "forward",
   "indexBounds":
   {
    "projectId": [
     "[ObjectId('58211791ea2640000c7a3fe6'), ObjectId('58211791ea2640000c7a3fe6')]"
    ]
   },
   "keysExamined": 28338,
   "dupsTested": 0,
   "dupsDropped": 0,
   "seenInvalidated": 0
  }
 }
}

可知,events集合并没有为projectId与createAt建立复合索引,因此IXSCAN阶段采用的是projectId索引,其nReturned为28338; FETCH阶段需要根据createAt进行过滤,其nReturned为20853,过滤掉了7485个文档;另外,IXSCAN与FETCH阶段的executionTimeMillisEstimate分别为30ms和27815ms,因此基本上所有时间都消耗在了FETCH阶段,这应该是读取硬盘导致的。

创建复合索引

没有为projectId和createAt创建复合索引是个尴尬的错误,赶紧补救一下:

db.events.createIndex({projectId:1,createTime:-1},{background: true})

在生产环境构建索引这种事最好是晚上做,这个命令一共花了大概7个小时吧!background设为true,指的是不要阻塞数据库的其他操作,保证数据库的可用性。但是,这个命令会一直占用着终端,这时不能使用CTRL + C,否则会终止索引构建过程。

复合索引创建成果之后,前文的查询就快了很多(仅保留executionStats):

db.javascriptevents.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})
"executionStats":
{
 "executionSuccess": true,
 "nReturned": 0,
 "executionTimeMillis": 47,
 "totalKeysExamined": 20854,
 "totalDocsExamined": 0,
 "executionStages":
 {
  "stage": "COUNT",
  "nReturned": 0,
  "executionTimeMillisEstimate": 50,
  "works": 20854,
  "advanced": 0,
  "needTime": 20853,
  "needYield": 0,
  "saveState": 162,
  "restoreState": 162,
  "isEOF": 1,
  "invalidates": 0,
  "nCounted": 20853,
  "nSkipped": 0,
  "inputStage":
  {
   "stage": "COUNT_SCAN",
   "nReturned": 20853,
   "executionTimeMillisEstimate": 50,
   "works": 20854,
   "advanced": 20853,
   "needTime": 0,
   "needYield": 0,
   "saveState": 162,
   "restoreState": 162,
   "isEOF": 1,
   "invalidates": 0,
   "keysExamined": 20854,
   "keyPattern":
   {
    "projectId": 1,
    "createAt": -1
   },
   "indexName": "projectId_1_createTime_-1",
   "isMultiKey": false,
   "isUnique": false,
   "isSparse": false,
   "isPartial": false,
   "indexVersion": 1
  }
 }
}

以上就是MongoDB中如何使用复合索引,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网数据库频道。

您可能感兴趣的文档:

--结束END--

本文标题: MongoDB中如何使用复合索引

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

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

猜你喜欢
  • MongoDB中如何使用复合索引
    本篇文章给大家分享的是有关MongoDB中如何使用复合索引,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是复合索引?复合索引,即Compo...
    99+
    2024-04-02
  • MongoDB中如何使用唯一索引
    这篇文章将为大家详细讲解有关MongoDB中如何使用唯一索引,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。我们用的是MongoDB数据存储用户信息,用户表中...
    99+
    2024-04-02
  • MongoDB数据库中如何使用索引
    本篇文章为大家展示了MongoDB数据库中如何使用索引,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。  MongoDB的各种索引有哪些用法  1.单列索引  在字段...
    99+
    2024-04-02
  • 【MongoDB学习笔记21】MongoDB的复合索引
    索引的值是按照一定顺序排列的,因此使用索引键对文档进行搜索排序比较快;但是只有首先使用索引进行排序时,索引才有用;例如下面的排序里,“username”上的索引就没有起作用:> db.use...
    99+
    2024-04-02
  • Oracle中如何创建复合索引
    这期内容当中小编将会给大家带来有关Oracle中如何创建复合索引,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。 1.对于复合索引,在Wher...
    99+
    2024-04-02
  • MongoDB复合索引引发的灾难是怎样的
    这期内容当中小编将会给大家带来有关MongoDB复合索引引发的灾难是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。前情提要11月末我司商品服务的MongoDB主库曾...
    99+
    2024-04-02
  • mysql复合索引如何创建
    在MySQL中,可以使用CREATE INDEX语句来创建复合索引。复合索引是基于多个列的索引,可以提高查询性能。 创建复合索引的语...
    99+
    2023-10-28
    mysql
  • Mysql中复合索引使用规则有哪些
    这篇文章主要介绍了Mysql中复合索引使用规则有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 联合索引验证:从左向右发挥作用索引:(...
    99+
    2024-04-02
  • Mysql的复合索引如何生效
    目录背景认识复合索引最左匹配原则字段顺序的影响复合索引可以替代单一索引吗?小结背景 最近频繁出现慢SQL导致系统性能问题,于是决定针对索引进行一些优化。一些表结构本身已经有了不少索...
    99+
    2024-04-02
  • MongoDB中怎么使用唯一索引
    这篇文章将为大家详细讲解有关MongoDB中怎么使用唯一索引,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。 唯一索引的主要目的是...
    99+
    2024-04-02
  • mongodb如何添加索引
    mongodb添加索引的方法:在mongodb可以使用createIndex()方法来创建添加索引,语法格式为:“db.collection.createIndex(keys, options)”,这里keys指的是你创建添加索引的值,并且...
    99+
    2024-04-02
  • Oracle中复合索引与空值的索引怎么用
    这篇文章给大家分享的是有关Oracle中复合索引与空值的索引怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在QQ群里讨论一个SQL优化的问题,语句大致如下:selec...
    99+
    2024-04-02
  • MongoDB中如何减少索引碎片
    在MongoDB中,可以通过以下方法来减少索引碎片: 定期进行索引重建:定期对索引进行重建可以帮助减少索引碎片。在MongoDB...
    99+
    2024-04-19
    MongoDB
  • MongoDB中索引怎么用
    小编给大家分享一下MongoDB中索引怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧! 在任何数...
    99+
    2024-04-02
  • 复合索引使用的先决条件
    PS:懒得重新编辑图片了,直接把我从51上的日志拷过来了。背景:今天,接到一个项目的项目经理电话,告之说生产环境有几个查询超级慢,就是查询单张表的数据,查询条件也很简单,但是加了索引以后并没有走索引,依然还...
    99+
    2024-04-02
  • MySQL中如何使用索引
    这篇文章给大家介绍MySQL中如何使用索引,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。mysql采用b+树的方式存储索引信息。b+树结构如下:说一下b+树的几个特点:叶子节点(最下面...
    99+
    2024-04-02
  • mongodb数据库中怎么使用索引
    这篇文章将为大家详细讲解有关mongodb数据库中怎么使用索引,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。      &nb...
    99+
    2024-04-02
  • mongodb如何查看表索引
    在MongoDB中,可以使用以下方法来查看表的索引:1. 使用`db.collection.getIndexes()`方法:该方法可...
    99+
    2023-09-11
    mongodb
  • MongoDB 索引简单使用技巧
    先在数据库上增加一些数据,输入下面的命令:for(var i=1;i<10;i++) db.customers.insert({name:"jordan"+i,country:"American"})...
    99+
    2024-04-02
  • C#中如何使用索引器
    本篇文章给大家分享的是有关C#中如何使用索引器,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、索引器的定义C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作