本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
规划缓存过滤器
计划缓存过滤器(也称为索引筛选器)限制了允许查询计划器针对特定查询形状考虑的一组索引。当查询匹配具有过滤器的形状时,计划者仅从该过滤器中命名的索引中选择计划,而不是评估集合中的每个索引。
查询形状是查询谓词、排序规范和集合命名空间的组合,谓词值已标准化。两个仅值不同的查询共享一个形状,因此一个过滤器可以覆盖所有查询。在的输出中planCacheListFilters,标准化值显示为"@"。
计划缓存过滤器为您提供了一种固定计划的方法,这样您就可以避免由于数据分布或索引变化而选择较慢的索引。过滤器具有以下属性:
无需更改应用程序。过滤器使用数据库命令设置并应用到服务器上,因此无需部署代码即可缓解回归。这是与 a 的主要区别
hint,后者必须在每个呼叫站点添加。范围是一个查询形状。形状由集合、谓词和排序定义,而不是由完整查询定义,因此过滤器仅限制与该形状匹配的查询。对同一馆藏的其他查询仍在正常计划之中。
这种变化是持久的。过滤器在实例重启和引擎修补后仍然有效。删除该集合会移除其过滤器。
这种变化是可逆的。清除过滤器会使形状恢复到基于成本的正常计划,因此过滤器是一种在调查根本原因时稳定工作负载的低风险方法。
支持的 命令
计划缓存过滤器需要计划器版本 2.0 或更高版本。筛选条件可以应用的命令取决于计划器版本和 Amazon DocumentDB 次要版本,如下表所示。
| 命令 | 最低计划器版本 | 最低次要版本 |
|---|---|---|
|
2.0 |
5.0.0、8.0.0 |
|
2.0 |
5.0.0、8.0.0 |
|
2.0 |
5.0.2、8.0.2 |
|
2.0 |
5.0.2、8.0.2 |
|
2.0 |
5.0.2、8.0.2 |
|
3.0 |
8.0.0 |
|
3.0 |
8.0.2 |
注意
Planner 版本 3.0 仅在 Amazon DocumentDB 8.0 中可用。由于distinct和aggregate命令需要计划器版本 3.0,因此这两个命令的计划缓存筛选器仅在 Amazon DocumentDB 8.0 中可用。在支持计划器版本 2.0 的 Amazon DocumentDB 5.0 中find,筛选器适用于countupdate、delete、和findAndModify命令。
对于aggregate命令,形状取自管道的前端:前端$match提供过滤器,$sort紧随其后的是过滤器。$skip并且$limit不会影响形状,超过该点的阶段也不会影响形状。
Amazon DocumentDB 会在规划管道之前对其进行优化,并将计划缓存筛选器与优化的管道进行匹配。因此,计划者看到的筛选和排序可能与您编写的阶段有所不同。例如,管道前面的两个相邻$match阶段合并为一个$and谓词:
db.orders.aggregate([ { $match: { status: "SHIPPED" } }, { $match: { region: "us-east-1" } }, { $group: { _id: "$customerId", total: { $sum: "$amount" } } } ])
计划者看到一个$match开启{$and: [{status: ...}, {region: ...}]},因此必须在该组合谓词上设置过滤器。
db.runCommand({planCacheSetFilter: "orders", query: { $and: [ { status: "SHIPPED" }, { region: "us-east-1" } ] }, indexes: [ "status_1_region_1" ]})
在$lookup或$graphLookup阶段的外部集合上设置的过滤器驱动用于扫描该外国集合的索引。
支持的查询形状
从 Amazon DocumentDB 5.0.2 和 8.0.2 开始,以下运算符可以出现在具有计划缓存筛选器的查询形状中:
$text。搜索字符串是标准化的,因此仅在搜索词上不同的查询共享一个形状。$near和$nearSphere两个运算符均归一化为相同的形状。坐标$minDistance、和$maxDistance不是形状的一部分。$geoWithin和$geoIntersectsGeoJSON 几何类型是形状的一部分,因此Polygon查询和MultiPolygon查询是不同的形状。坐标不是形状的一部分。$regex,直接在球场上使用时。模式和选项已标准化,因此同一字段上的所有正则表达式共享一个形状。
为使用$jsonSchema、$expr或$sampleRate的查询形状设置过滤器失败,错误代码为 303。对于以下形状,它也失败了。使用其中任何一种的查询都是在计划中不使用过滤器的:
$regex嵌套在里面$in$nin、或$all。将正则表达式与文字值混合在一起的数组无法根据索引进行评估,因此永远无法对该形状应用过滤器。的排序规范
{$natural: 1}。$natural排序请求集合扫描,这与将计划器限制为索引不兼容。
设置、列出和清除筛选器
要设置计划缓存筛选器,请使用planCacheSetFilter命令。query和sort字段共同指定了查询形状,并indexes列出了允许规划者为该形状考虑的索引名称。
db.runCommand({ planCacheSetFilter: <collection>, query: <query>, sort: <sort>, // optional indexes: [ <index1>, <index2>, ...], comment: <any> // optional })
例如,以下命令将计划器限制为不进行排序的形状{a: {$eq: "@"}, b: {$eq: "@"}}的a_1索引:
db.runCommand({planCacheSetFilter: "foo", query: { a: 1, b: 1 }, sort: {}, indexes: [ "a_1" ]})
为已有滤镜的形状设置滤镜将替换现有滤镜。运行以下两个命令后,形状上的过滤器{a: {$eq: "@"}, b: {$eq: "@"}}为["b_1"]:
db.runCommand({planCacheSetFilter: "foo", query: { a: 1, b: 1 }, sort: {}, indexes: [ "a_1" ]}) db.runCommand({planCacheSetFilter: "foo", query: { a: 6, b: 10 }, sort: {}, indexes: [ "b_1" ]})
要列出集合中的所有过滤器,请使用以下planCacheListFilters命令:
db.runCommand({planCacheListFilters: <collection>})
输出显示每个存储的形状及其标准化值和允许的索引列表:
{ "filters" : [ { "query" : { "a" : { "$eq" : "@" } }, "sort" : { }, "indexes" : [ "a_1" ] }, { "query" : { "a" : { "$gt" : "@" } }, "sort" : { "a" : 1 }, "indexes" : [ "a_1_b_1" ] } ], "ok" : 1 }
运算符被保留,只有值被替换为"@"。隐式相等性{a: 1},例如以其显式形式列出{"a": {"$eq": "@"}},$in数组的元素作为一个整体替换,为{"a": {"$in": "@"}}。列出未进行排序的筛选器集时,sort文档为空。
要移除过滤器,请使用planCacheClearFilters命令。
db.runCommand({ planCacheClearFilters: <collection>, query: <query pattern>, // optional sort: <sort specification>, // optional comment: <any> // optional })
要清除集合中的所有过滤器,请省略这两个query和sort:
db.runCommand({planCacheClearFilters: "foo"})
同时提供这两个query选项,则仅sort清除排序完全相同的过滤器。中的值query被归一化掉,因此任何代表值都有效。要仅清除未进行排序的过滤器,请传递一个空sort文档:
db.runCommand({planCacheClearFilters: "foo", query: {a: 1}, sort: {a: 1}}) db.runCommand({planCacheClearFilters: "foo", query: {a: 1}, sort: {}})
验证是否应用了过滤器
explain输出包括两个报告计划缓存过滤器状态的字段:
indexFilterSet是true当集合上存在查询形状的过滤器时。indexFilterAppliedtrue仅当使用该过滤器列表中的索引计划查询时。
从 Amazon DocumentDB 5.0.2 和 8.0.2 开始,每条支持计划缓存筛选器的命令都会报告这些字段,并在这些命令的分析器输出中报告这些字段。在早期的次要版本中,它们仅针对find命令进行了报告。有关阅读 explain 输出的更多信息,请参阅查询计划分析。
过滤器无需应用即可匹配查询形状。如果过滤器列表中的所有索引都无法为查询提供服务,则计划者在没有过滤器的情况下重新规划,并选择成本指数。在这种情况下,indexFilterSet是true、indexFilterApplied是false。当命名索引不覆盖查询的字段、命名索引不存在或$regex形状无法生成索引边界(例如未锚定或不区分大小写的模式)时,就会发生这种情况。
示例
以下示例使用orders集合和customers集合作为$lookup示例,索引如下:
db.orders.createIndex({ status: 1 }) // status_1 db.orders.createIndex({ status: 1, orderDate: 1 }) // status_1_orderDate_1 db.orders.createIndex({ customerId: 1 }) // customerId_1 db.customers.createIndex({ customerId: 1 }) // customerId_1
示例:将查找查询固定到复合索引
假设一个按以下条件进行过滤status和排序的查询orderDate:
db.orders.find({ status: "SHIPPED" }).sort({ orderDate: 1 })
复合索引同时status_1_orderDate_1满足谓词和排序,因此无需单独排序。如果计划者改为选择status_1,则查询必须对其结果进行排序,这会增加一个SORT阶段,并且随着匹配订单数量的增加而变得更加昂贵。要将计划器限制在此形状的复合索引范围内,请执行以下操作:
db.runCommand({planCacheSetFilter: "orders", query: { status: "SHIPPED" }, sort: { orderDate: 1 }, indexes: [ "status_1_orderDate_1" ]})
因为值是按形状归一化的,所以这个过滤器还覆盖{ status: "PENDING" }{ status: "CANCELLED" }、以及所有其他相同排序status的值。它不使用不同的排序或不包含排序的同一个谓词,它们是不同的形状。使用以下命令确认过滤器已生效explain:
db.orders.find({ status: "SHIPPED" }).sort({ orderDate: 1 }).explain()
输出报告indexFilterSet: true和indexFilterApplied: true,以及IXSCAN阶段名称status_1_orderDate_1。
示例:将更新固定到选择性索引
更新必须先找到要修改的文档,然后才能写入它们,并且该查询使用索引的方式与读取相同。当一个谓词可以由多个索引提供服务时,规划者选择的索引决定更新检查的文档数量。
db.orders.updateMany( { customerId: 4815, status: "PENDING" }, { $set: { status: "CANCELLED" } } )
两者customerId_1兼而有之status_1,都可以使用这个谓词。要将更新固定到customerId_1:
db.runCommand({planCacheSetFilter: "orders", query: { customerId: 4815, status: "PENDING" }, indexes: [ "customerId_1" ]})
写入命令的过滤器需要 Amazon DocumentDB 5.0.2 或 8.0.2 和 Planner 版本 2.0 或更高版本。使用以下更新explain进行验证:
db.runCommand({explain: {update: "orders", updates: [{ q: { customerId: 4815, status: "PENDING" }, u: { $set: { status: "CANCELLED" } }, multi: true }]}})
制胜计划是一个不停IXSCAN的UPDATE阶段customerId_1。同样的过滤器适用于delete使用此查询形状的findAndModify命令,因为该形状不包括写入类型。
示例:固定聚合管道和 $lookup 外国集合
对于聚合,形状取自前一$match阶段,$sort紧接其后。除此之外的阶段,例如$group或$project,不属于形状的一部分。
db.orders.aggregate([ { $match: { status: "SHIPPED" } }, { $group: { _id: "$customerId", total: { $sum: "$amount" } } } ])
在$match谓词上设置的过滤器驱动用于读orders取的索引:
db.runCommand({planCacheSetFilter: "orders", query: { status: "SHIPPED" }, indexes: [ "status_1" ]})
过滤器也适用于$lookup或$graphLookup阶段加入的集合。过滤器是在外部集合上设置的,而不是在管道运行的集合上设置的。在以下管道中,orders是外部集合,customers是外国集合:
db.orders.aggregate([ { $lookup: { from: "customers", localField: "customerId", foreignField: "customerId", as: "customer" } } ])
联接会对每个外部文档进行customers一次探测,因此在那里选择的索引会被重复应用,其成本在整个管道中成倍增加。要将该扫描固定到特定索引,请customers为联接密钥的相等形状设置过滤器:
db.runCommand({planCacheSetFilter: "customers", query: { customerId: 1 }, indexes: [ "customerId_1" ]})
因为值是按形状归一化的,所以1上面的值是一个占位符,任何值都会生成相同的过滤器。当应用国外集合筛选条件时,顶层indexFilterSet和indexFilterApplied字段报告true以及所选筛选条件的索引将显示在获胜计划的外部。开启过滤器aggregate需要亚马逊 DocumentDB 8.0.2 和计划器版本 3.0。
带有提示和索引更改的行为
计划缓存过滤器的优先级高于
hint。Amazon DocumentDB 首先应用过滤器,然后在过滤器的允许索引列表中应用提示。如果提示命名了过滤器允许的索引,则使用该索引。如果提示命名了过滤器不允许的索引,则该提示将被忽略,计划者根据成本在允许的索引中进行选择。无论是命名索引还是给出其键模式,这两种提示表单的行为方式都相同。例如,如果集合
foo的索引为a_1b_1a_1_b_1、和,并且您使用索引列表在形状{query: {a: {$eq: "@"}, b: {$eq: "@"}}, sort: {a: 1}}上设置了过滤器["a_1", "a_1_b_1"],则运行会db.foo.find({ a: 10, b: 20 }).sort({a: 1}).hint({ a: 1 })使用a_1,因为该过滤器是在提示中命名的,并且是过滤器允许的。删除索引不会修改过滤器。过滤器保留索引名称。尽管缺少该索引,但规划却忽略了它。如果过滤器列表中的另一个索引可以为查询提供服务,则过滤器仍然适用。只有当命名索引都不可用时,才会
indexFilterApplied报告false。重新创建同名索引使其再次符合条件。删除一个集合会移除该集合的所有过滤器。