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

Hai*_*Ali 2 database indexing mongodb 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}
Run Code Online (Sandbox Code Playgroud)

我必须搜索我的收藏在哪里

user id == 1 and slug == 'one-slug'
Run Code Online (Sandbox Code Playgroud)

在这个集合中,slug 对用户 ID 来说是唯一的。也就是说,用户 id 1 只能有一个值为“one-slug”的 slug。

我知道 user_id 由于其基数高而应优先考虑,但是 slug 呢?因为它在大多数时候也是独一无二的。我也无法理解升序和降序索引,或者在这种情况下它如何影响性能或我应该在这个集合中使用的正确顺序。

我已经阅读了一些,但我无法理解它,特别是对于我的场景。听到别人的声音会很棒。

kev*_*adi 10

您可以将 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}
Run Code Online (Sandbox Code Playgroud)

单字段索引

现在如果你这样做:

db.collection.createIndex({a:1})
Run Code Online (Sandbox Code Playgroud)

该索引大致如下所示:

[index a:1]
1: {a:1} --> 2, 4
2: {a:2} --> 3, 5
3: {a:3} --> 1
Run Code Online (Sandbox Code Playgroud)

注意三件重要的事情:

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

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

db.collection.find().sort({a:1})
Run Code Online (Sandbox Code Playgroud)

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

db.collection.find().sort({a:-1})
Run Code Online (Sandbox Code Playgroud)

唯一的区别是您反向遍历索引。

因为b根本不在索引中,所以在查询关于b.

复合指数

在复合索引中,例如:

db.collection.createIndex({a:1, b:1})
Run Code Online (Sandbox Code Playgroud)

这意味着您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
Run Code Online (Sandbox Code Playgroud)

注意:

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

使用此索引,您可以执行如下查询:

db.collection.find({a:2}).sort({b:1})
Run Code Online (Sandbox Code Playgroud)

它可以很容易地找到a:2然后向前走索引。鉴于该索引,您不能这样做

db.collection.find().sort({b:1})
db.collection.find({b:1})
Run Code Online (Sandbox Code Playgroud)

在这两个查询中,您无法轻松找到,b因为它遍布整个索引(即不在连续条目中)。但是你可以这样做:

db.collection.find({a:2}).sort({b:-1})
Run Code Online (Sandbox Code Playgroud)

因为您基本上可以找到它们的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
Run Code Online (Sandbox Code Playgroud)

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

指数的{a:1, b:1}意思是sort by a, then within each a, sort the b values。如果您然后执行 a find({a:2}).sort({b:1}),则索引知道所有的a=2位置。在 的这个块中a=2b将按升序排序(根据索引规范),以便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
Run Code Online (Sandbox Code Playgroud)

查找({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
Run Code Online (Sandbox Code Playgroud)

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

查询规划器解释

您可以使用db.collection.explain().find(....). 基本上,如果您看到 a stageof COLLSCAN,则表示没有使用或可用于查询的索引。有关命令输出的详细信息,请参阅解释结果