深入理解MongoDB的复合索引


当前第2页 返回上一页

不妨使用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
  }
 }
}

可知,count操作使用了projectId和createAt的复合索引,因此非常快,只花了46ms,性能提升了将近600倍!!!对比使用复合索引前后的结果,发现totalDocsExamined从28338降到了0,表示使用复合索引之后不再需要去查询文档,只需要扫描索引就好了,这样就不需要去访问磁盘了,自然快了很多。

参考

  • MongoDB 复合索引
  • MongoDB文档:Compound Indexes

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。


打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...