MongoDB复合索引-排序顺序重要吗?

时间:2018-07-04 10:44:21

标签: database mongodb indexing nosql compound-index

我最近潜入mongodb从事我的一个项目。 我一直在阅读索引,对于一个很小的集合,我知道这没什么大不了的,但是当它增长时,如果没有正确的索引和查询就会出现性能问题。

让我说我有这样的收藏

{user_id:1,slug:'one-slug'}
{user_id:1,slug:'another-slug'}
{user_id:2,slug:'one-slug'}
{user_id:3,slug:'just-a-slug}

我必须在

的位置搜索我的收藏集
user id == 1 and slug == 'one-slug'

在此集合中,子段对于用户ID是唯一的。 也就是说,用户ID 1只能具有“ one-slug”值中的一个。

我知道,由于user_id的基数高,因此应该给它优先级,但是会怎么样?由于它在大多数情况下都是独一无二的。我也无法把头放在升序和降序索引上,或者在这种情况下或者它将在此集合中使用的正确顺序如何影响性能。

我已经读了一些,但我无法将其包裹住,特别是对于我的情况。听到别人的消息真是太棒了。

2 个答案:

答案 0 :(得分:3)

您可以将MongoDB单字段索引视为一个数组,其中包含指向文档位置的指针。例如,如果您有一个带有的集合(请注意,该序列是故意乱序的):

[collection]
1: {a:3, b:2}
2: {a:1, b:2}
3: {a:2, b:1}
4: {a:1, b:1}
5: {a:2, b:2}

单字段索引

现在,如果您这样做:

db.collection.createIndex({a:1})

索引大致如下:

[index a:1]
1: {a:1} --> 2, 4
2: {a:2} --> 3, 5
3: {a:3} --> 1

请注意三件重要的事情:

  • a升序排序
  • 每个入口均指向相关文档所在的位置
  • 索引仅记录a字段的值。索引中根本不存在b字段

因此,如果您执行以下查询:

db.collection.find().sort({a:1})

它所要做的就是从上到下遍历索引,获取并输出条目所指向的文档。请注意,您也可以从底部开始浏览索引,例如:

db.collection.find().sort({a:-1})

唯一的区别是您反向移动索引。

由于b根本不在索引中,因此在查询有关b的任何内容时都无法使用索引。

化合物索引

在复合索引中,例如:

db.collection.createIndex({a:1, b:1})

这意味着您要先按a排序,然后再按b排序。索引如下:

[index a:1, b:1]
1: {a:1, b:1} --> 4
2: {a:1, b:2} --> 2
3: {a:2, b:1} --> 3
4: {a:2, b:2} --> 5
5: {a:3, b:2} --> 1

请注意:

  • 索引是按a
  • 排序的
  • 在每个a中,您有一个已排序的b
  • 您有5个索引条目,而在前面的单字段示例中只有3个

使用该索引,您可以执行类似以下查询:

db.collection.find({a:2}).sort({b:1})

它可以轻松找到a:2的位置,然后向前索引。 给出该索引,您将无法执行

db.collection.find().sort({b:1})
db.collection.find({b:1})

在两个查询中,由于b遍及整个索引(即不在连续的条目中),因此很难找到。但是,您可以这样做:

db.collection.find({a:2}).sort({b:-1})

因为您基本上可以找到a:2的位置,并将b条目向后移动。

编辑:在评论中澄清@marcospgp的问题:

如果从排序表的角度来看,使用索引{a:1, b:1}来满足find({a:2}).sort({b:-1})的可能性实际上是有意义的。例如,索引{a:1, b:1}可以认为是:

a | b
--|--
1 | 1
1 | 2
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2

查找({a:2})。sort({b:1})

索引{a:1, b:1}表示sort by a, then within each a, sort the b values。如果然后执行find({a:2}).sort({b:1}),则索引会知道所有a=2的位置。在a=2的这一块中,b将按照升序排序(根据索引规范),因此查询find({a:2}).sort({b:1})可以通过以下方式满足:

a | b
--|--
1 | 1
1 | 2
2 | 1 <-- walk this block forward to satisfy
2 | 2 <-- find({a:2}).sort({b:1})
2 | 3 <--
3 | 1
3 | 2

查找({a:2})。sort({b:-1})

由于索引可以向前或向后移动,因此遵循类似的过程,但在末尾稍作改动:

a | b
--|--
1 | 1
1 | 2
2 | 1  <-- walk this block backward to satisfy
2 | 2  <-- find({a:2}).sort({b:-1})
2 | 3  <--
3 | 1
3 | 2

索引可以向前或向后走的事实是使查询find({a:2}).sort({b:-1})能够使用索引{a:1, b:1}的关键所在。

查询计划者说明

您可以使用db.collection.explain().find(....)查看查询计划者的计划。基本上,如果您看到stage中的COLLSCAN,则没有使用索引或可将其用于查询。有关命令输出的详细信息,请参见explain results

答案 1 :(得分:0)

[由于缺乏声誉而无法发表评论]

  

索引方向仅在排序时才重要。

并非完全准确:即使查询本身不需要任何顺序,某些查询在具有特定方向索引的情况下也会更快(排序仅用于结果)。例如,使用日期条件的查询:搜索昨天订阅的用户在索引上使用desc方向要比使用asc方向或不使用索引更快。

  

{user_id:1,slug:1}与{slug:1,user_id:1}之间的差异

mongo将在第一个字段上进行过滤,然后在索引中第一个字段匹配(依此类推...)的第二个字段上进行过滤。必须首先使用限制性更强的字段才能真正改善查询