- MongoDB官方文档中文版
- MongoDB中文手册说明
- MongoDB简介
- 安装 MongoDB
- The mongo Shell
- MongoDB CRUD 操作
- 聚合
- 数据模型
- 事务
- 索引
- 安全
- 安全检查列表
- 启用访问控制
- 身份验证
- 基于角色的访问控制
- TLS / SSL(传输加密)
- 静态加密
- 客户端字段级加密
- 审计
- 网络和配置强化
- 实现字段级别修订
- 安全参考
- 附录
- 变更流
- 复制
- 分片
- 分片键
- 哈希分片
- 范围分片
- 区
- 管理分片区
- 按位置细分数据
- 用于更改SLA或SLO的分层硬件
- 按应用或客户细分数据
- 仅插入工作负载的分布式本地写入
- 管理分片区
- 使用块进行数据分区
- 在分片集群中拆分数据块
- 管理
- 存储
- 存储引擎
- 日志记录
- 管理日志记录
- GridFS
- FAQ:MongoDB 存储
- 存储引擎
- 参考
- 运算符
- 查询与映射运算符
- 更新运算符
- 聚合管道阶段
- 聚合管道操作符
- $abs (aggregation)
- $acos (aggregation)
- $acosh (aggregation)
- $add (aggregation)
- $addToSet (aggregation)
- $allElementsTrue (aggregation)
- $and (aggregation)
- $anyElementTrue (aggregation)
- $arrayElemAt (aggregation)
- $arrayToObject (aggregation)
- $asin (aggregation)
- $asinh (aggregation)
- $atan (aggregation)
- $atan2 (aggregation)
- $atanh (aggregation)
- $avg (aggregation)
- $ceil (aggregation)
- $cmp (aggregation)
- $concat (aggregation)
- $concatArrays (aggregation)
- $cond (aggregation)
- $convert (aggregation)
- $cos (aggregation)
- $dateFromParts (aggregation)
- $dateToParts (aggregation)
- $dateFromString (aggregation)
- $literal (aggregation)
- 查询修饰符
- 数据库命令
- 聚合命令
- 地理空间命令
- 查询和写操作命令
- 查询计划缓存命令
- 认证命令
- 用户管理命令
- 角色管理命令
- 复制命令
- 分片命令
- 会话命令
- 管理命令
- 诊断命令
- 免费监控命令
- 系统事件审计命令
- mongo Shell 方法
- 集合方法
- db.collection.aggregate()
- db.collection.bulkWrite()
- db.collection.copyTo()
- db.collection.count()
- db.collection.countDocuments()
- db.collection.estimatedDocumentCount()
- db.collection.createIndex()
- db.collection.createIndexes()
- db.collection.dataSize()
- db.collection.deleteOne()
- db.collection.deleteMany()
- db.collection.distinct()
- db.collection.drop()
- db.collection.dropIndex()
- db.collection.dropIndexes()
- db.collection.ensureIndex()
- db.collection.explain()
- db.collection.find()
- db.collection.findAndModify()
- db.collection.findOne()
- db.collection.findOneAndDelete()
- db.collection.findOneAndReplace()
- db.collection.findOneAndUpdate()
- db.collection.getIndexes()
- db.collection.getShardDistribution()
- db.collection.getShardVersion()
- db.collection.insert()
- db.collection.insertOne()
- db.collection.insertMany()
- db.collection.isCapped()
- db.collection.latencyStats()
- db.collection.mapReduce()
- db.collection.reIndex()
- db.collection.remove()
- db.collection.renameCollection()
- db.collection.replaceOne()
- db.collection.save()
- db.collection.stats()
- db.collection.storageSize()
- db.collection.totalIndexSize()
- db.collection.totalSize()
- db.collection.update()
- db.collection.updateOne()
- db.collection.updateMany()
- db.collection.watch()
- db.collection.validate()
- 词汇表
- 默认的MongoDB端口
- 默认的MongoDB读/写关注
- 服务器会话
- MongoDB驱动
- FAQ
- 联系我们
- 更多资料
- [快学Mongo]
- [Mongo问题讨论区]
- [Mongo 驱动使用手册]
- 本书使用 GitBook 发布
聚合管道优化
聚合管道优化
在本页面
聚合管道操作具有优化阶段,该阶段试图重塑管道以改善性能。
要查看优化程序如何转换特定聚合管道,请在db.collection.aggregate()方法中包含explain选项。
优化可能会在不同版本之间发生变化。
投影优化
聚合管道可以确定它是否仅需要文档中的字段的子集来获得结果。如果是这样,管道将只使用那些必需的字段,减少通过管道的数据量。
管道序列优化
($project or $unset or $addFields or $set) + $match 序列优化
对于包含投影阶段($project或$unset或$addFields或$set)后跟$match阶段的聚合管道,MongoDB 将$match阶段中不需要在投影阶段计算的值的任何过滤器移动到投影前的新$match阶段。
如果聚合管道包含多个投影 and/or $match阶段,MongoDB 会为每个$match阶段执行此优化,将每个$match过滤器移动到过滤器不依赖的所有投影阶段之前。
考虑以下阶段的管道:
{ $addFields: {
maxTime: { $max: "$times" },
minTime: { $min: "$times" }
} },
{ $project: {
_id: 1, name: 1, times: 1, maxTime: 1, minTime: 1,
avgTime: { $avg: ["$maxTime", "$minTime"] }
} },
{ $match: {
name: "Joe Schmoe",
maxTime: { $lt: 20 },
minTime: { $gt: 5 },
avgTime: { $gt: 7 }
} }
优化器将$match阶段分成四个单独的过滤器,一个用于$match查询文档中的每个键。然后优化器将每个筛选器移动到尽可能多的投影阶段之前,根据需要创建新的$match阶段。鉴于此示例,优化程序生成以下优化管道:
{ $match: { name: "Joe Schmoe" } },
{ $addFields: {
maxTime: { $max: "$times" },
minTime: { $min: "$times" }
} },
{ $match: { maxTime: { $lt: 20 }, minTime: { $gt: 5 } } },
{ $project: {
_id: 1, name: 1, times: 1, maxTime: 1, minTime: 1,
avgTime: { $avg: ["$maxTime", "$minTime"] }
} },
{ $match: { avgTime: { $gt: 7 } } }
$match过滤器{ avgTime: { $gt: 7 } }
取决于$project阶段来计算avgTime
字段。 $project阶段是此管道中的最后一个投影阶段,因此avgTime
上的$match过滤器无法移动。
maxTime
和minTime
字段在$addFields阶段计算,但不依赖于$project阶段。优化器为这些字段上的过滤器创建了一个新的$match阶段,并将其放在$project阶段之前。
$match过滤器{ name: "Joe Schmoe" }
不使用在$project或$addFields阶段计算的任何值,因此它在两个投影阶段之前被移动到新的$match阶段。
[success] 注意
优化后,过滤器
{ name: "Joe Schmoe" }
位于管道开头的$match阶段。这具有额外的好处,即允许聚合在最初查询集合时在name
字段上使用索引。有关更多信息,请参见管道操作符和索引。
$sort + $match 序列优化
如果序列中带有$sort后跟$match,则$match会移动到$sort之前,以最大程度的减少要排序的对象的数量。例如,如果管道包含以下阶段:
{ $sort: { age : -1 } },
{ $match: { status: 'A' } }
在优化阶段,优化程序将序列转换为以下内容:
{ $match: { status: 'A' } },
{ $sort: { age : -1 } }
$redact + $match 序列优化
如果可能,当管道的$redact阶段紧在$match阶段之后时,聚合有时可以在$redact阶段之前添加$match阶段的一部分。如果添加的$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
/ $unset
+ $skip
序列优化
3.2版本中的新功能。
当有一个$project
或$unset
之后跟有$skip
序列时,$skip
会移至$project
之前。例如,如果管道包括以下阶段:
{ $sort: { age : -1 } },
{ $project: { status: 1, name: 1 } },
{ $skip: 5 }
在优化阶段,优化器将序列转换为以下内容:
{ $sort: { age : -1 } },
{ $skip: 5 },
{ $project: { status: 1, name: 1 } }
管道聚合优化
如果可能,优化阶段将一个管道阶段合并到其前身。通常,合并发生在任何序列重新排序优化之后。
$sort
+ $limit
合并
Mongodb 4.0版本的改变。
当一个$sort
先于$limit
,优化器可以聚结$limit
到$sort
,如果没有中间阶段的修改文件(例如,使用数$unwind
,$group
)。如果有管道阶段会更改和阶段之间的文档数,则MongoDB将不会合并$limit
到 。$sort
$sort
$limit
例如,如果管道包括以下阶段:
{ $sort : { age : -1 } },
{ $project : { age : 1, status : 1, name : 1 } },
{ $limit: 5 }
在优化阶段,优化器将序列合并为以下内容:
{
"$sort" : {
"sortKey" : {
"age" : -1
},
"limit" : NumberLong(5)
}
},
{ "$project" : {
"age" : 1,
"status" : 1,
"name" : 1
}
}
这样,排序操作就可以仅在执行过程中保持最高n
结果,这n
是指定的限制,MongoDB仅需要将n
项目存储在内存中 [1]。有关更多信息,请参见$ sort运算符和内存。
用
$skip
进行序列优化如果
$skip
在$sort
和$limit
阶段之间有一个阶段,MongoDB将合并$limit
到该$sort
阶段并增加该$limit
值$skip
。有关示例,请参见 $ sort + $ skip + $ limit序列。
$limit
+ $limit
合并
当$limit
紧接着另一个时 $limit
,两个阶段可以合并为一个阶段 $limit
,其中限制量为两个初始限制量中的较小者。例如,管道包含以下序列:
{ $limit: 100 },
{ $limit: 10 }
然后,第二$limit
级可以聚结到第一 $limit
阶段,并导致在单个$limit
阶段,即限制量10
是两个初始极限的最小100
和10
。
{ $limit: 10 }
$skip
+ $skip
合并
当$skip
紧跟另一个$skip
,这两个阶段可合并成一个单一的$skip
,其中跳过量为总和的两个初始跳过量。例如,管道包含以下序列:
{ $skip: 5 },
{ $skip: 2 }
然后,第二$skip
阶段可以合并到第一 $skip
阶段,并导致单个$skip
阶段,其中跳过量7
是两个初始限制5
和的总和2
。
{ $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版中的新功能。
当a $unwind
立即紧随其后 $lookup
,并且在 领域$unwind
运行时,优化程序可以将其合并 到阶段中。这样可以避免创建较大的中间文档。as
$lookup
$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 }
}
}
例子
$limit $skip $limit $skip 序列
止于Mongodb4.0
{ $limit: 100 },
{ $skip: 5 },
{ $limit: 10 },
{ $skip: 2 }
$skip $limit 序列优化反转{ $skip: 5 }
和{ $limit: 10 }
阶段的位置并增加限制量:
{ $limit: 100 },
{ $limit: 15},
{ $skip: 5 },
{ $skip: 2 }
然后,优化器将两个$limit阶段合并为一个$limit阶段,将两个$skip阶段合并为一个$skip阶段。结果序列如下:
{ $limit: 15 },
{ $skip: 7 }
有关详细信息,请参阅$limit $limit 合并和$skip $skip 合并。
[success] 可以看看
译者:李冠飞
校对:李冠飞
Copyright © 上海锦木信息技术有限公司 all right reserved,由 MongoDB汉化小组 提供技术支持文件修订时间: 2020-10-11 20:53:05