我最近为了我的一个项目潜入了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'
在此集合中,slug 对于用户 ID 是唯一的。 也就是说,用户 ID 1 只能有一个值为"one-slug"的 slug。
我知道user_id由于其高基数而应该优先考虑,但是蛞蝓呢?因为它在大多数情况下也是独一无二的。我也无法理解升序和降序索引,或者在这种情况下它将如何影响性能或我应该在此集合中使用的正确顺序。
我已经读过一些,但我无法理解它,特别是对于我的场景。听到其他人的声音会很棒。
您可以将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
find({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
find({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(....)
查看查询计划程序的计划。基本上,如果您看到COLLSCAN
的stage
,则没有使用索引或可用于查询。有关命令输出的详细信息,请参阅说明结果。
[由于缺乏声誉而无法发表评论]
索引方向仅在排序时重要。
不完全精确:某些查询使用特定的方向索引可以更快,即使查询本身不需要顺序(排序仅用于结果(。例如,使用日期条件的查询:在索引上使用desc方向搜索昨天订阅的用户将比使用asc方向或无索引更快。
{user_id:1,slug:1} 和 {slug:1,user_id:1} 之间的差异
MONGO 将过滤第一个字段,然后在索引中具有第一个字段匹配(依此类推...(的第二个字段。限制性更强的字段必须排在第一位才能真正改进查询