为什么单个索引比两个键的查询中的复合索引更快? (MongoDB,多键)

时间:2013-11-29 12:31:40

标签: python mongodb indexing multikey

在查询同一文档的两个字段时,我创建了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"  
}

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

QUERY 1:

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

millis:237

nscanned:101226

QUERY 2:

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

millis:1131

nscanned:306715

QUERY 3:

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

millis:935

nscanned:306715

QUERY 4:

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

millis:1165

nscanned:306715

QUERY 1扫描较少的文档,可能是更快的原因。有人可以帮助我理解为什么它比具有复合索引的查询表现更好?因此,何时使用复合指数比单一指数更好?

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

提前致谢。

更新的问题(回应Sammaye和Philipp)

这是完整解释()

的结果
"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()

2 个答案:

答案 0 :(得分:2)

您创建了一个复合索引(名为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上的索引好。

答案 1 :(得分:0)

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

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

这是一个众所周知的带有多键索引的警告,为什么你应该小心这样抛弃它们。

我认为第三个查询速度太慢的原因是因为多键索引不能支持indexOnly游标,因此MongoDB无法在那里使用覆盖查询。