Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs 菜单
Docs 主页
/ /

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
}
]

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

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 }
)
[
{
_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 }

后退

Strategies

在此页面上