翻译或纠错本页面

聚合管道的优化

Aggregation pipeline operations have an optimization phase which attempts to reshape the pipeline for improved performance.

To see how the optimizer transforms a particular aggregation pipeline, include the explain option in the db.collection.aggregate() method.

Optimizations are subject to change between releases.

预测优化

聚合管道可以检测到是否仅使用文档中的一部分字段就可以完成聚合。如果是的话,管道就可以仅使用这些必要的字段,从而减少进入管道的数据量。

Pipeline Sequence Optimization

$sort + $match 顺序优化

如果你的管道中, $sort 后面跟着 $match ,把 $match 移到 $sort 前面可以减少需要排序的对象个数。例如,如果管道中有以下两个部分:

{ $sort: { age : -1 } },
{ $match: { status: 'A' } }

During the optimization phase, the optimizer transforms the sequence to the following:

{ $match: { status: 'A' } },
{ $sort: { age : -1 } }

$skip + $limit 顺序优化

如果你的管道中, $skip 后面跟着 $limit ,优化器会把 $limit 移到 $skip 前面,这个时候, $limit 的值会加上 $skip 的个数。

例如,如果管道由以下部分组成:

{ $skip: 10 },
{ $limit: 5 }

During the optimization phase, the optimizer transforms the sequence to the following:

{ $limit: 15 },
{ $skip: 10 }

对于类似 $sort + $limit 合并 ,例如 $sort + $skip + $limit ,优化器允许你做很多优化。详情请查看 $sort + $limit 合并 ,也可以在 $sort + $skip + $limit 顺序 中查看例子。

对于在 分片集合上的聚合 ,优化器可以减少从每个分片返回的文档个数。

$redact + $match 顺序优化

如果可能,当管道中 $redact 阶段后面紧接着有 $match 操作,聚合有时候会添加一个 $match$redact 前面。如果在管道在一开始有 $match ,聚合操作在查询时可以使用索引,以减少进入到管道中的文档个数。更多详情请查看 管道操作符和索引

例如,如果管道由以下部分组成:

{ $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } },
{ $match: { year: 2014, category: { $ne: "Z" } } }

优化器可以在 $redact 之前增加相同的 $match

{ $match: { year: 2014 } },
{ $redact: { $cond: { if: { $eq: [ "$level", 5 ] }, then: "$$PRUNE", else: "$$DESCEND" } } },
{ $match: { year: 2014, category: { $ne: "Z" } } }

$project + $skip or $limit Sequence Optimization

3.2 新版功能.

When you have a sequence with $project followed by either $skip or $limit, the $skip or $limit moves before $project. For example, if the pipeline consists of the following stages:

{ $sort: { age : -1 } },
{ $project: { status: 1, name: 1 } },
{ $limit: 5 }

During the optimization phase, the optimizer transforms the sequence to the following:

{ $sort: { age : -1 } },
{ $limit: 5 }
{ $project: { status: 1, name: 1 } },

This optimization allows for more opportunities for $sort + $limit 合并, such as with $sort + $limit sequences. See $sort + $limit 合并 for details on the coalescence.

管道合并优化

优化器可以在管道开始之前合并其他的管道。一般情况下,合并发生在所有顺序优化之后。

$sort + $limit 合并

如果 $sort$limit 前面,优化器可以把 $limit 合并在 $sort 内部。此时如果指定了限定返回 n 个结果,那么排序操作仅需要维护最前面的 n 个结果,MongoDB只需要在内存中存储 n 个元素 [1]。更多信息请查看 $sort Operator and Memory

[1]

allowDiskUse 设置为 truen 数目超过 聚合的内存限制 时,优化依旧会进行。

$limit + $limit 合并

$limit 操作后面还有一个 $limit 操作,这两步可以合并成一个单独的 $limit 操作,此时限制的个数是前面两个限制个数中较小的值。例如,一个管道操作包含有如下操作序列:

{ $limit: 100 },
{ $limit: 10 }

此时,第二个 $limit 操作可以合并到第一个 $limit 操作中,最后生成一个 $limit 操作并且限制个数为初始两个限制个数 10010 中的较小的一个 10

{ $limit: 10 }

$skip + $skip 合并

$skip 操作后面还有一个 $skip 操作,这两步可以合并成一个单独的 $skip 操作,此时跳过的个数是前面两个跳过个数的和。例如,一个管道操作包含有如下操作序列:

{ $skip: 5 },
{ $skip: 2 }

此时,第二个 $skip 操作可以合并到第一个 $skip 操作中,最后生成一个 $skip 操作并且跳过个数为初始两个跳过个数 52 的相加值 7

{ $skip: 7 }

$match + $match 合并

$match 操作后面还有一个 $match 操作,可以将这两步中的条件使用 $and 表达式合并成一个单独的 $match 操作。例如,一个管道操作包含有如下操作序列:

{ $match: { year: 2014 } },
{ $match: { status: "A" } }

此时,第二个 $match 操作可以合并到第一个 $match 操作中,最后生成一个 $match 操作。

{ $match: { $and: [ { "year" : 2014 }, { "status" : "A" } ] } }

$lookup + $unwind Coalescence

3.2 新版功能.

When a $unwind immediately follows another $lookup, and the $unwind operates on the as field of the $lookup, the optimizer can coalesce the $unwind into the $lookup stage. This avoids creating large intermediate documents.

For example, a pipeline contains the following sequence:

{
  $lookup: {
    from: "otherCollection",
    as: "resultingArray",
    localField: "x",
    foreignField: "y"
  }
},
{ $unwind: "$resultingArray"}

The optimizer can coalesce the $unwind stage into the $lookup stage. If you run the aggregation with explain option, the explain output shows the coalesced stage:

{
  $lookup: {
    from: "otherCollection",
    as: "resultingArray",
    localField: "x",
    foreignField: "y",
    unwinding: { preserveNullAndEmptyArrays: false }
  }
}

例子

下面的一些顺序优化的例子可以结合顺序重排和合并的优势。一般来说,合并优化在所有的顺序重排之后进行。

$sort + $skip + $limit 顺序

一个依次包含 $sort$skip$limit 的管道:

{ $sort: { age : -1 } },
{ $skip: 10 },
{ $limit: 5 }

首先,优化器执行 $skip + $limit 顺序优化 ,转换后的顺序如下:

{ $sort: { age : -1 } },
{ $limit: 15 }
{ $skip: 10 }

经过 $skip + $limit 顺序优化 后, $limit 的限定个数会增长。详情请查看 $skip + $limit 顺序优化

重排后的 $sort$limit 前面,这时可以将这两个阶段合并来降低排序时需要的内存大小。详情请查看 $sort + $limit 合并

$limit + $skip + $limit + $skip 顺序

管道中如果交替包含了 $limit$skip :

{ $limit: 100 },
{ $skip: 5 },
{ $limit: 10 },
{ $skip: 2 }

The $skip + $limit 顺序优化 reverses the position of the { $skip: 5 } and { $limit: 10 } stages and increases the limit amount:

{ $limit: 100 },
{ $limit: 15},
{ $skip: 5 },
{ $skip: 2 }

优化器会把两个 $limit 合并为一个 $limit ,两个 $skip 合并为一个 $skip 。合并后的结果如下:

{ $limit: 15 },
{ $skip: 7 }

详情请查看 $limit + $limit 合并$skip + $skip 合并

参见

explain option in the db.collection.aggregate()