对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。

ESR(相等、排序、范围)指南

一个复合索引引用多个字段,可以显着缩短查询响应时间。

在大多数情况下,应用 ESR(相等、排序、范围)准则来排列索引键可创建更高效的复合索引。

确保相等字段始终排在第一位。将相等字段放在第一位可以使其余索引字段按排序顺序排列。接下来,根据索引的特定需求,选择使用排序字段还是范围字段:

  • 如果避免内存中排序很重要,请将排序字段放在范围字段之前 (ESR)

  • 如果查询中的范围谓词具有很强的选择性,请将其放在排序字段 (ERS) 之前

有关优化查询的更多信息,请参阅 explain 和 查询计划。

提示

要强制MongoDB使用特定索引,请在测试索引时使用游标.hint()(mongosh方法)。

本页上的示例使用来自 sample_mflix 数据集的数据。有关如何将此数据集加载到您的自管理 MongoDB 部署中的详细信息,请参阅 加载示例数据集。如果您对示例数据库进行了任何修改,您可能需要删除并重新创建数据库,才能运行此页面上的示例。

“相等”系指单个值的精确匹配。以下精确匹配查询扫描 movies 集合以查找 title 字段 Equilibrium 精确匹配的文档。

db.movies.find( { title: "Equilibrium" }, { title: 1, year: 1, cast: 1 })
[ { _id: ObjectId('573a13a3f29313caabd0c8d2'), year: 2002, title: 'Equilibrium', cast: [ 'Christian Bale', 'Dominic Purcell', 'Sean Bean', 'Christian Kahrmann' ] }]
db.movies.find( { title: { $eq: "Equilibrium" } }, { title: 1, year: 1, cast: 1 })
[ { _id: ObjectId('573a13a3f29313caabd0c8d2'), year: 2002, title: 'Equilibrium', cast: [ 'Christian Bale', 'Dominic Purcell', 'Sean Bean', 'Christian Kahrmann' ] }]

索引搜索可有效利用精确匹配来减少检查的索引键数量。相等字段必须放在第一位。

一个索引可以有多个相等键。它们可以以任何相对彼此的顺序出现,但所有相等键必须位于任何排序或范围字段之前。

等值匹配的选择性越强,索引查询的效率就越高。

“排序”确定结果的顺序。要避免内存中排序,请将排序字段放在索引中的范围之前。

仅当查询在排序键之前的所有前缀键上包含相等条件时,索引支持对其键的子集进行排序操作。有关详细信息,请参阅索引的排序和非前缀子集。

以下示例查询 movies集合中 directors字段包含 "David Lynch" 的电影。输出按 year 排序:

db.movies.find( { directors: "David Lynch" }, { title: 1, year: 1 } ).sort( { year: 1 } )
[ { _id: ObjectId('573a1397f29313caabce77d4'), title: 'The Elephant Man', year: 1980 }, { _id: ObjectId('573a1398f29313caabce9091'), title: 'Dune', year: 1984 }, { _id: ObjectId('573a1398f29313caabce9e12'), title: 'Blue Velvet', year: 1986 }, { _id: ObjectId('573a1399f29313caabced630'), year: 1992, title: 'Twin Peaks: Fire Walk with Me' }, { _id: ObjectId('573a139af29313caabcf00f0'), year: 1997, title: 'Lost Highway' }, { _id: ObjectId('573a139ef29313caabcfbc0e'), year: 1999, title: 'The Straight Story' }, { _id: ObjectId('573a139ef29313caabcfbc36'), title: 'Mulholland Drive', year: 2001 }, { _id: ObjectId('573a13b4f29313caabd40a54'), title: 'Inland Empire', year: 2006 }]

要提高查询性能,请对 directors 和 year 字段创建索引:

db.movies.createIndex( { directors: 1, year: 1 } )
  • directors 是第一个键,因为它是相等匹配。

  • year 按照与查询相同的顺序 (1) 建立索引。

“范围”过滤器会扫描字段。此扫描不要求精确匹配,因此范围过滤器会松散绑定到索引键。为提高查询效率,应尽可能缩小范围边界,并使用等值匹配来限制必须扫描的文档数量。

范围筛选器类似如下内容:

db.movies.find( { runtime: { $gte: 1000 } }, { title: 1, runtime: 1, year: 1, plot: 1 } )
[ { _id: ObjectId('573a1397f29313caabce69db'), plot: 'The economic and cultural growth of Colorado spanning two centuries from the mid-1700s to the late-1970s.', runtime: 1256, title: 'Centennial', year: 1978 }, { _id: ObjectId('573a1399f29313caabcee1aa'), plot: 'A documentary on the history of the sport with major topics including Afro-American players, player/team owner relations and the resilience of the game.', runtime: 1140, title: 'Baseball', year: 1994 }]
db.movies.find( { year: { $lt: 1900 } }, { title: 1, year: 1, plot: 1 })
[ { _id: ObjectId('573a139cf29313caabcf560f'), plot: 'Two people kiss.', title: 'The Kiss', year: 1896 }, { _id: ObjectId('573a13a0f29313caabd041db'), plot: 'Two people kiss.', title: 'The Kiss', year: 1896 }]
db.movies.find( { type: { $ne: "movie" } }, { title: 1, year: 1, type: 1 } ).limit(5)
[ { _id: ObjectId('573a1395f29313caabce2f03'), title: 'The Forsyte Saga', year: 1967, type: 'series' }, { _id: ObjectId('573a1396f29313caabce520d'), title: 'Scenes from a Marriage', year: 1973, type: 'series' }, { _id: ObjectId('573a1396f29313caabce5b86'), title: 'Ironiya sudby, ili S legkim parom!', year: 1975, type: 'series' }, { _id: ObjectId('573a1397f29313caabce6378'), title: 'Sybil', year: 1976, type: 'series' }, { _id: ObjectId('573a1397f29313caabce6443'), title: 'Jesus of Nazareth', year: 1977, type: 'series' }]

如果查询中的范围谓词选择性很强,请将其放在排序字段之前,以减少已排序文档的数量并允许进行内存中排序。

要避免内存中排序,请将范围过滤放在排序谓词之后。有关内存中排序的更多信息,请参阅 cursor.allowDiskUse()。

  • 不等式操作符,如 $ne 或 $nin 是范围操作符,而不是相等操作符。

  • $regex 是个范围操作符。

  • $in:

    • $in 单独使用时,它是一个执行一系列相等匹配的相等运算符。

    • 当 $in 与 .sort() 一起使用时:

      • 如果 $in 的大量元素少于 201 个,则使用 SORT_MERGE 阶段,按照为索引指定的排序顺序展开并合并这些元素。这提高了小型数组的性能。在本例中,$in 类似于具有 ESR 的相等谓词。

      • 如果 $in 具有 201 个或更多元素,则这些元素的排序方式类似于范围操作符。在这种情况下,小型数组的性能提升无法实现。索引中的后续字段不可能用于排序,并且 $in 类似于具有 ESR 的范围谓词。

      • 如果您通常将 $in 操作符用于小型数组,请将其尽早包含在索引规范中。如果您通常使用大型数组,请在需要范围谓词的位置加入 $in 操作符。

注意

对于所有MongoDB版本,不能保证 201大量元素处的 $in 行为更改保持不变。

以下查询在 movies集合中搜索 directors字段为 "David Lynch" 且 runtime字段小于 130 分钟的电影。结果按 year 排序:

db.movies.find( { directors: "David Lynch", runtime: { $lt: 130 } }, { title: 1, year: 1, runtime: 1 }).sort( { year: 1 } )
[ { _id: ObjectId('573a1397f29313caabce77d4'), runtime: 124, title: 'The Elephant Man', year: 1980 }, { _id: ObjectId('573a1398f29313caabce9e12'), runtime: 120, title: 'Blue Velvet', year: 1986 }, { _id: ObjectId('573a139ef29313caabcfbc0e'), year: 1999, title: 'The Straight Story', runtime: 112 }]

该查询包含 ESR 指南的所有内容:

  • directors: "David Lynch" 是基于相等的匹配

  • runtime: { $lt: 130 } 是基于范围的匹配

  • year 用于排序

根据 ESR 指南,示例查询的最佳索引为:

{ directors: 1, year: 1, runtime: 1 }