【问题标题】:Why is a single index faster than a compound index in a query for two keys? (MongoDB, multi-key)为什么在查询两个键时单个索引比复合索引快? (MongoDB,多键)
【发布时间】:2013-12-15 15:48:48
【问题描述】:

在查询同一文档的两个字段时,我创建了 4 个索引来测试我的集合中的查询性能,其中一个是数组(需要多键索引)。其中两个索引是单一的,两个是复合的。

我很惊讶,因为使用单一索引之一比使用复合索引获得更好的性能。我期望使用复合索引获得最佳性能,因为我知道它索引了两个字段,从而可以更快地查询。

这些是我的索引:

{    "v" : 1, 
     "key" : { "_id" : 1 }, 
     "ns" : "bt_twitter.mallorca.mallorca", 
     "name" : "_id_"  
}, 
{    "v" : 1, 
     "key" : { "epoch_creation_date" :1 }, 
     "ns" : "bt_twitter.mallorca.mallorca", 
     "name" : "epoch_creation_date_1"  
}, 
{     "v" : 1, 
      "key" : { "related_hashtags" : 1 }, 
      "ns" : "bt_twitter.mallorca.mallorca", 
      "name" : "related_hashtags_1"  
},  
{     "v" : 1, 
      "key" : { "epoch_creation_date" : 1, "related_hashtags" : 1 }, 
      "ns" : "bt_twitter.mallorca.mallorca", 
      "name" : "epoch_creation_date_1_related_hashtags_1"  
}

我的查询和性能指标是(提示参数显示每次查询使用的索引):

问题 1:

active_collection.find(
    {'epoch_creation_date': {'$exists': True}}, 
    {"_id": 0, "related_hashtags":1}
).hint([("epoch_creation_date", ASCENDING)]).explain()

毫:237

nscanned: 101226

问题 2:

active_collection.find(
    {'epoch_creation_date': {'$exists': True}}, 
    {"_id": 0, "related_hashtags": 1}
).hint([("related_hashtags", ASCENDING)]).explain()

毫:1131

nscanned: 306715

问题 3:

active_collection.find(
     {'epoch_creation_date': {'$exists': True}},
     {"_id": 0, "related_hashtags": 1}
).hint([("epoch_creation_date", ASCENDING), ("related_hashtags", ASCENDING)]).explain()

毫:935

nscanned: 306715

问题 4:

active_collection.find(
     {'epoch_creation_date': {'$exists': True}}, 
     {"_id": 0, "related_hashtags": 1}
).hint([("related_hashtags", ASCENDING),("epoch_creation_date", ASCENDING)]).explain()

毫:1165

nscanned: 306715

QUERY 1 扫描的文档较少,可能是什么原因扫描得更快。有人可以帮我理解为什么它比使用复合索引的查询执行得更好吗?那么,什么时候使用复合索引比使用单一索引更好?

我正在阅读 mongo 文档,但这些概念让我难以消化。

提前致谢。

更新的问题(针对 Sammaye 和 Philipp)

这是一个完整的explain()的结果

"cursor" : "BtreeCursor epoch_creation_date_1",
"isMultiKey" : false,
"n" : 101226,
"nscannedObjects" : 101226,
"nscanned" : 101226,
"nscannedObjectsAllPlans" : 101226,
"nscannedAllPlans" : 101226,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 242,
"indexBounds" : {u'epoch_creation_date': [[{u'$minElement': 1}, {u'$maxElement': 1}]]

},
"server" : "vmmongodb:27017"

对于以下查询:

active_collection.find(
{'epoch_creation_date': {'$exists': True}}, 
{"_id": 0, "related_hashtags":1})
.hint([("epoch_creation_date", ASCENDING)]).explain()

【问题讨论】:

  • 您需要告诉我们这些索引是如何定义的。
  • 嗨 Phillipp,您的意思是我是如何创建索引的?我以 active_collection.create_index([("epoch_creation_date", ASCENDING),("related_hashtags", ASCENDING)]) 为例
  • 我的意思是你用来创建索引的 ensureIndex 调用。
  • 嗯,再看一次查询 2 没有任何意义,为什么它与查询 3 和查询 4 ​​相同的 nscanned,实际上没有理由为什么查询 4 ​​应该与查询 3 具有相同的 nscanned跨度>
  • 正如@Philipp 所说,您实际拥有哪些索引?

标签: python mongodb indexing multikey


【解决方案1】:

您创建了一个复合索引(名为epoch_creation_date_1_related_hashtags_1),但您没有在这些提示中使用它。取而代之的是,您正在以不同的顺序使用您还创建的两个单字段索引(related_hashtags_1epoch_creation_date_1)。

在这两个索引中,只有epoch_creation_date_1 有效,因为您没有查询这两个字段。您只查询一个,这是'epoch_creation_date': {'$exists': True}。您使用{"_id": 0, "related_hashtags":1} 执行的字段过滤是在该查询找到的文档上完成的。到那时,索引就不再有用了。这意味着related_hashtags 上的任何索引都无法提高此查询的性能。复合索引(当您实际使用时)可能比没有索引要好,但不如仅在 epoch_creation_date 上的索引。

【讨论】:

  • 这是有道理的,但一直感到困惑。阅读 Mongo 文档:docs.mongodb.org/manual/tutorial/… 他们说codedb.users.ensureIndex( { status: 1, user: 1 } ) 将是db.users.find( { status: "A" }, { user: 1, _id: 0 } ) 的一个很好的索引
  • @OscarMoya 但看起来这不是你所做的。这就是为什么我要求您提供用于创建索引的 ensureIndex 命令。
  • 我按照api.mongodb.org/python/current/tutorial.html#indexing 的说明创建索引。我开始认为 PyMongo 不能支持为多键字段创建这样的复合索引(在任何情况下听起来都不太可能)。 PyMongo 的文档对此不是很明确。
  • @OscarMoya 根据您给我们的索引声明更新了答案。
  • 是的,现在这对我来说很有意义。非常感谢你们俩!我将勾选这个答案作为解决问题的答案。最后一个问题,如果你不介意的话……有没有一种方法可以让我在堆栈溢出中也感谢 Sammaye 的帮助??
【解决方案2】:

好的,在阅读了更多问题后,我理解了这个问题。多键索引将写入一个索引条目 PER 多值。这意味着,如果每个文档每个 related_hashtags 有 3 个值,那么您的索引实际上是大小的 3 倍,并且要扫描的值的数量是 3 倍(如果我的数学加起来...)。

nscanned 是查看文档次数的计数器(注意计数器,不是查看的唯一文档的特定数量),这意味着由于多键索引,您必须扫描大约 3 倍的数量(相同的)文档,您通常会在第一个查询中使用。

这是一个关于多键索引的已知警告,以及为什么你应该小心像这样扔掉它们。

我相信第三个查询之所以这么慢是因为多键索引不支持indexOnly游标,所以MongoDB无法在那里使用覆盖查询。

【讨论】:

  • 你是对的。其他读者可以在以下位置看到:docs.mongodb.org/manual/tutorial/… 文档说:“如果:集合中任何文档中的任何索引字段包含数组,则索引无法覆盖查询。如果索引字段是数组,则索引成为多键索引索引,不能支持覆盖查询”
猜你喜欢
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
  • 1970-01-01
  • 2016-10-19
  • 1970-01-01
相关资源
最近更新 更多