MongoDB数据库学习-聚集-聚集管道优化。
聚集管道优化
聚集管道操作有一个优化阶段,试图重组管道以提高性能。
要查看优化器如何变换一个特定的聚集管道,在db.collection.aggregate()方法中包括explain选项。
优化操作因版本的不同而不同。
投影优化
聚集管道可以确定它是否只需要文档字段的一个子集来获取结果,如果是的化,管道只会使用这些必需的字段,减少的通过管道的数据量。
管道序列优化
$sort + $match序列优化
当$sort后面跟着$match的时候,$match会被移到$sort前,以最小化排序的数据量。例如,如果管道由以下步骤组成:
{
$sort: {
age : -1 }
}
,{
$match: {
status: '
A'
}
}
在优化阶段,优化器把序列变成如下:
{
$match: {
status: '
A'
}
}
,{
$sort: {
age : -1 }
}
$skip+$limit序列优化
当$skip后面有$limit的时候,$limit会被移到$skip前面。重排之后,$limit的值要增加$skip的量。
例如,如果管道由以下步骤组成:
{
$skip: 10 }
,{
$limit: 5 }
在优化阶段,优化器会把序列变成如下:
{
$limit: 15 }
,{
$skip: 10 }
对于$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" }
}
}
优化器可以把$match阶段添加到$redact阶段之前:
{
$match: {
year: 2014 }
}
,{
$redact: {
$cond: {
if: {
$eq: [ "$level", 5 ] }
, then: "$$PRUNE", else: "$$DESCEND" }
}
}
,{
$match: {
year: 2014, category: {
$ne: "Z" }
}
}
$project+$skip或$limit序列优化
版本3.2新增。
当$project后面跟着$skip或者$limit的时候,$skip或$limit可以移到$project之前。例如,如果管道由以下阶段组成:
{
$sort: {
age : -1 }
}
,{
$project: {
status: 1, name: 1 }
}
,{
$limit: 5 }
在优化阶段,优化器把序列变成:
{
$sort: {
age : -1 }
}
,{
$limit: 5 }
{
$project: {
status: 1, name: 1 }
}
,
管道合并优化
如果有可能,优化阶段会把一个管道阶段合并到它前面那一个去。一般地,合并操作出现在任何一个序列重组优化之后。
$sort+$limit合并
当$sort直接放在$limit前面的时候,优化器可以把$limit合并到$sort里面。这使得排序操作只保留前n个结果,n是limit所指定的值,MongoDB只需要在内存中保留n条数据。查看$sort操作符和内存获取更多信息。
$limit+$limit合并
当一个$limit后直接跟着另一个$limit时,这两个阶段可以合并成一个$limit,limit数量是两个初始limit数量的较小值。例如,一个管道包含如下序列:
{
$limit: 100 }
,{
$limit: 10 }
第二个$limit阶段可以合并到第一个$limit阶段,结果只剩一个$limit阶段,limit数量是10,是这两个limit中的较小值。
{
$limit: 10 }
$skip+$skip合并
当一个$skip后面直接跟着另一个$skip时,这两个阶段可以合并成一个$skip阶段,skip的数量是两个skip数量之和。例如,一个管道包含如下序列:
{
$skip: 5 }
,{
$skip: 2 }
第二个$skip可以合并至第一个$skip,结果只有一个$skip阶段,skip的数量是7,即这两个skip的数量之和:
{
$skip: 7 }
$match+$match合并
当一个$match后面直接跟着另一个$match时,这两个阶段可以合并成一个$match,用$and操作符把它们的条件组合起来。例如,一个管道包含如下序列:
{
$match: {
year: 2014 }
}
,{
$match: {
status: "A" }
}
第二个$match可以合并至第一个$match,结果只有一个$match阶段:
{
$match: {
$and: [ {
"year" : 2014 }
, {
"status" : "A" }
] }
}
$lookup+$unwind合并
版本3.2新增。
当一个$unwind直接跟着$lookup时,并且$unwind直接对$lookup的as字段操作,优化器就可以把$unwind合并至$lookup阶段内,这避免了创建大量的中间文档。
例如,一个管道包含如下序列:
{
$lookup: {
from: "otherCollection", as: "resultingArray", localField: "x", foreignField: "y" }
}
,{
$unwind: "$resultingArray"}
优化器可以把$unwind阶段合并至$lookup阶段。如果你用explain选项运行这个聚集,explain输出如下的合并阶段:
{
$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之前了,管道就可以把这两个阶段合并,以减少排序操作过程中的内存占用。更多请查看$sor$limit合并。
$limit+$skip+$limit+$skip序列
一个包含$limit和$skip阶段交替的序列的管道。
{
$limit: 100 }
,{
$skip: 5 }
,{
$limit: 10 }
,{
$skip: 2 }
$skip+$limit序列优化把{$skip: 5}与{$limit: 10}的位置交换以下,增加limit的量:
{
$limit: 100 }
,{
$limit: 15}
,{
$skip: 5 }
,{
$skip: 2 }
优化器然后把这两个$limit阶段合并成一个$limit阶段,把两个$skip阶段合并成一个$skip阶段,结果序列如下:
{
$limit: 15 }
,{
$skip: 7 }