ESR(相等、排序、范围)规则
引用多个字段的索引是复合索引。复合索引可大幅缩短查询响应时间。
索引键与文档字段相对应。大多数情况下,应用 ESR(相等、排序、范围)规则来排列索引键有助于创建更有效的复合索引。
本页介绍了 ESR 规则。有关优化查询的更多信息,请参阅 explain
和查询计划。
提示
要强制 MongoDB 使用特定索引,请在测试索引时使用 cursor.hint()。
相等
“相等”系指单个值的精确匹配。以下精确匹配查询扫描 cars
集合以查找 model
字段 Cordoba
精确匹配的文档。
db.cars.find( { model: "Cordoba" } ) db.cars.find( { model: { $eq: "Cordoba" } } )
索引搜索可高效利用精确匹配来限制为完成某一查询而需检查的文档数量。请在您的索引中优先放置需要精确匹配的字段。
一个索引可能有多个用于精确匹配的键。用于等值匹配的索引键可以以任何顺序出现。但是,如要使用索引满足等值匹配,所有用于精确匹配的索引键必须放在其他索引字段之前。MongoDB 的搜索算法让您无需以特定顺序排列精确匹配字段。
精确匹配应具有选择性,即只匹配少数文档。为了减少扫描的索引键数量,请在等值匹配的测试中确保判断条件能筛选掉至少 90% 的文档。
Sort
“排序”决定结果的顺序。排序操作在等值匹配之后,因为等值匹配会减少需要排序的文档数量。在等值匹配之后进行排序还能让 MongoDB 进行非阻塞排序。
当查询字段是索引键的子集时,索引可以支持排序操作。仅当查询包含排序键之前的所有前缀键的相等条件时,才支持对索引键子集进行排序操作。有关更多信息,请参阅索引的排序和非前缀子集。
以下示例将查询 cars
集合。输出将按 model
进行排序:
db.cars.find( { manufacturer: "GM" } ).sort( { model: 1 } )
要提高查询性能,请对 manufacturer
和 model
字段创建索引:
db.cars.createIndex( { manufacturer: 1, model: 1 } )
manufacturer
是第一个键,因为它是相等匹配。model
按照与查询相同的顺序 (1
) 建立索引。
范围
“范围”过滤器会扫描字段。此扫描不要求精确匹配,因此范围过滤器会松散绑定到索引键。为提高查询效率,应尽可能缩小范围边界,并使用等值匹配来限制必须扫描的文档数量。
范围筛选器类似如下内容:
db.cars.find( { price: { $gte: 15000} } ) db.cars.find( { age: { $lt: 10 } } ) db.cars.find( { priorAccidents: { $ne: null } } )
MongoDB 无法对范围过滤器的结果进行索引排序。将范围过滤器置于排序谓词之后,以便 MongoDB 可使用非阻塞索引排序。有关阻塞排序的更多信息,请参阅 cursor.allowDiskUse()
。
其他注意事项
$regex
是个范围操作符。$in
单独使用时,它是一个执行一系列相等匹配的相等运算符。当
$in
与.sort()
一起使用时:如果
$in
的数组元素少于 200,则这些元素将展开,然后按照为索引指定的排序顺序进行合并。这会提高小型数组的性能。$in
类似于具有 ESR 的相等谓词。如果
$in
具有 200 个或更多元素,则这些元素的排序方式类似于范围操作符。在这种情况下,小型数组没有实现性能提升。索引中的后续字段无法提供排序,并且$in
类似于具有 ESR 的范围谓词。如果通常将
$ins
用于小型数组,请在索引规范的前面位置包含$ins
。如果通常使用大型数组,请在包含范围谓词的位置包含$ins
。
注意
200 限制可能会发生变化,并且不能保证所有 MongoDB 版本都保持不变。
例子
以下查询在 cars
集合中搜索福特制造的价格超过 15,000 美元的车辆。结果按车型排序:
db.cars.find( { manufacturer: 'Ford', cost: { $gt: 15000 } } ).sort( { model: 1 } )
该查询包含了 ESR 规则中的所有元素:
manufacturer: 'Ford'
为基于相等的匹配操作cost: { $gt: 15000 }
为基于范围的匹配操作,并且model
用于排序
根据 ESR 规则,对于该示例查询,最优的索引为:
{ manufacturer: 1, model: 1, cost: 1 }
进一步讨论
许多 MongoDB 会议报告都深入讨论了 ESR 规则。