MongoDB 中的索引存储在 B 树结构中,其中每个索引条目都指向磁盘上的特定位置。使用 B 树结构还意味着 MongoDB 索引按排序顺序存储,始终按顺序遍历,并且 MongoDB 通过索引按排序顺序获取一系列文档的成本很低。
更新:B-tree 结构适用于 MMAPv1 存储引擎,但 WiredTiger 存储引擎的实现略有不同(自 MongoDB 3.2 起默认)。基本思想保持不变,按排序顺序遍历索引很便宜。
查询中的SORT 阶段(即内存中排序)限制为 32MB 的内存使用。如果SORT 阶段超过此限制,查询将失败。这个限制可以通过利用索引的排序特性来规避,这样 MongoDB 就可以返回带有 sort() 参数的查询,而无需执行内存排序。
让我们假设查询的形状是:
db.a.find({b:{$gt:100}, c:{$gt:200}}).sort(...)
集合 a 的索引为:
db.a.createIndex({b:1,c:1})
在查询中指定sort() 阶段时有两种可能的情况:
1. MongoDB 不能使用索引的排序特性,必须执行内存中的SORT 阶段。
这是查询不能使用“索引前缀”时的结果。例如:
db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({c:1})
在上面的查询中,索引{b:1,c:1}可用于:
- 为查询的
{b:{$gt:100}} 部分匹配b 大于100 的文档。
- 但是,不能保证返回的文档按照
c进行排序。
因此,MongoDB 只能执行内存排序。此查询的explain() 输出将具有SORT 阶段。这个SORT 阶段将被限制为 32MB 的内存使用。
2。 MongoDB 可以使用索引的排序特性。
这是查询使用的结果:
- 对与索引顺序匹配的键进行排序,并且
- 指定与索引相同的顺序(即索引
{b:1,c:1}可用于sort({b:1,c:1})或sort({b:-1,c:-1}),但不能用于sort({b:1,c:-1}))
例如:
db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({b:1})
在上面的查询中,索引{b:1,c:1}可用于:
- 为查询的
{b:{$gt:100}} 部分匹配b 大于100 的文档。
- 在这种情况下,MongoDB 可以保证返回的文档按照
b进行排序。
上述查询的explain() 输出将不具有SORT 阶段。此外,带有和不带有 sort() 的查询的 explain() 输出是相同的。从本质上讲,我们免费获得了sort()。
了解这个主题的一个有价值的资源是Optimizing MongoDB Compound Indexes。请注意,这篇博文写于 2012 年。虽然有些术语可能已经过时,但这篇博文的技术性仍然很重要。
后续问题更新
-
MongoDB 使用only one index for most queries。例如,为了避免查询中出现内存中的SORT 阶段
db.a.find({a:1}).sort({b:1})
索引必须同时覆盖a 和b 字段;例如需要一个复合索引,例如 {a:1,b:1}。您不能有两个单独的索引{a:1} 和{b:1},并期望{a:1} 索引用于相等部分,{b:1} 索引用于排序部分。在这种情况下,MongoDB 将选择两个索引之一。
因此,对结果进行排序是正确的,因为它们是按照索引的顺序查找和返回的。
-
为避免使用复合索引进行内存排序,索引的第一部分必须满足查询的相等部分,第二部分必须满足对查询的部分进行排序(如上面对(1)的解释所示)。
如果您有这样的查询:
db.a.find({}).sort({a:1})
索引{a:1,b:1} 可用于排序部分(因为您基本上是返回整个集合)。如果您的查询如下所示:
db.a.find({a:1}).sort({b:1})
同样的索引{a:1,b:1} 也可以用于查询的两个部分。另外:
db.a.find({a:1,b:1})
也可以使用相同的索引{a:1,b:1}
注意这里的模式:find() 后跟 sort() 参数遵循索引顺序 {a:1,b:1}。因此,复合索引必须按 equality -> sort 排序。
关于不同类型排序的更新
如果一个字段在文档之间具有不同的类型(例如,如果 a 在一个文档中是字符串,在其他文档中是数字,在另一个文档中是布尔值),那么排序如何进行?
答案是MongoDB BSON type comparison order。套用手册页,顺序是:
- MinKey(内部类型)
- 空
- 数字(整数、长整数、双精度、小数)
- 符号、字符串
- 对象
- 数组
- BinData
- 对象标识
- 布尔值
- 日期
- 时间戳
- 正则表达式
- MaxKey(内部类型)
所以从上面使用升序的例子来看,包含数字的文档将首先出现,然后是字符串,然后是布尔值。