mongodb - MongoDb复合索引优化

  显示原文与译文双语对照的内容
0 0

我试图建立一个需要很高写量的数据库,并且读取吞吐量合理。 我有一组分布式系统,它们将"事件"数据添加到数据库中。

当前,事件记录的id是 Guid 。 我一直在读到guid不会创建巨大的索引,因为它们的随机分布意味着最近的数据将分散。

下面是我想验证的第一个假设: 我假设我不想选择一个创建正确平衡树的_id,比如自动编号。 这将非常有益,因为 2最近的事件基本上是在磁盘上的。 这是正确的假设?

假设( 1 ) 是正确的,那么我试图找出最好的方法来生成这样的,。 我知道Mongo原生支持 ObjectId,对于可以将数据绑定到Mongo的应用程序很方便,但我的应用程序并不如此。 由于有多个系统,模拟"自动编号"字段是有问题的,因为在服务器端不支持自动编号。

In解决这个问题,我正在考虑将_id字段作为 { localId的复合键,其中本地id是 producer,因为producerId会生成 producer,因为它将使它的唯一。 ProducerId是我可以在生产者之间进行协商,这样它们就可以使用独特的up 。

下面是我的下一个问题: if的目标是从所有生产者获取最新数据,那么 { } 应该是首选键排序,因为 localId be localId and,因为localId是一个小集群,所以 2最近的事件是本地的。 如果我反转这个顺序,那么我的推理是如何最终看起来像如下所示:


 root
/| 
 p0 p1 p2
/| 
 e0..n e0..n e0..n

其中p#是生产者 Id,而e#是事件。 这似乎会把我的索引放到集群中,新的事件就不一定在彼此之间。 我对首选顺序的假设应该是( 请验证) 类似的:


 root
/| 
 e0 e1 e2
/| 
 p0..n p0..n p0..n

这似乎使最近的事件彼此靠近。 ( 我知道ldap使用b 树作索引,但是我只是在试图简化这里的视觉) 。

{ localId是一个常见的查询,用户可以通过它列出最近的事件,如 { producerId,localId } 实际上可以处理更多的事件。 我想我还需要将producerId作为字段添加到文档中,然后索引该查询,以便使这个查询能够处理 { localId 。

要确定我在这里的问题是什么,我想知道我是否正确地考虑了这个问题。

谢谢

时间: 原作者:

0 0

要回答你的问题:像这样的化合物: 如果只按b 进行查询,然后按以下方式排序,{a,b} 将在散列查询中结束。 但是它将使用索引进行排序。

如果使用文档而不是 ObjectId,_id将被索引但不使用,但它不是复合索引 !

例如:

给定集合'a'中的文档,并且没有其他索引:


{"_id" : {"e" : 1,"p" : 1 } }
{"_id" : {"e" : 1,"p" : 2 } }
{"_id" : {"e" : 2,"p" : 1 } }
{"_id" : {"e" : 1,"p" : 3 } }
{"_id" : {"e" : 2,"p" : 3 } }
{"_id" : {"e" : 2,"p" : 2 } }
{"_id" : {"e" : 3,"p" : 1 } }
{"_id" : {"e" : 3,"p" : 2 } }
{"_id" : {"e" : 3,"p" : 3 } }

这样的查询:


db.a.find({'_id.p' : 2}).sort({'_id.e' : 1}).explain()

将不使用索引:


{
"cursor" :"BasicCursor",
"nscanned" : 9,
"nscannedObjects" : 9,
"n" : 3,
"scanAndOrder" : true,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : { 
 }
}

只是因为这些文件被索引了。

如果你创建这样的索引:


db.a.ensureIndex({'_id.e' : 1, '_id.p' : 1})

然后再次查询:


db.a.find({'_id.p' : 2}).sort({'_id.e' : 1}).explain()

{
"cursor" :"BtreeCursor _id.e_1__id.p_1",
"nscanned" : 9,
"nscannedObjects" : 3,
"n" : 3,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
"_id.e" : [
 [
 {
"$minElement" : 1
 },
 {
"$maxElement" : 1
 }
 ]
 ],
"_id.p" : [
 [
 2,
 2
 ]
 ]
 }
}

它将查询索引( nscanned: 9 ) 由于排序,然后获取对象: 3,比 _id ( 。nscanned和nscannedObjects将是 9 ) 排序好。

文档. explain()

因此,对于高写入吞吐量( 超过 15k 秒写入),你可能会。 两个索引都将保证如果选项设置为。 但是,只有复合碎片键可以帮助你直接查询和不收集散集。

使用( {'_id 。e': 1'_id 。p: 1 }) 将所有查询直接路由到" _id.e"查询,但不是" _id.p"查询,因此这些查询将发送到每个主机,并在索引查找中结束,但可能会很快和。 如果要按"p"对这些查询进行群集,则必须将'_id.p'作为复合键的第一部分放置,如下所示:


{'_id.p' : 1, '_id.e' : 1}

所以所有"p"查询都是直接查询。 但是是的,这将把最近的事件分散到集群中。 因此使用基于时间的键的单独索引可以加速那些分散的查询。

我将生成一些示例数据,并在一个开发中使用两个碎片,并使用. explain() 选择碎片索引索引。

原作者:
...