

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 规划缓存过滤器
<a name="plan-cache-filter"></a>

计划缓存过滤器（也称为索引筛选器）限制了允许查询计划器针对特定查询形状考虑的一组索引。当查询匹配具有过滤器的形状时，计划者仅从该过滤器中命名的索引中选择计划，而不是评估集合中的每个索引。

查询形状是查询谓词、排序规范和集合命名空间的组合，谓词值已标准化。两个仅值不同的查询共享一个形状，因此一个过滤器可以覆盖所有查询。在的输出中`planCacheListFilters`，标准化值显示为`"@"`。

计划缓存过滤器为您提供了一种固定计划的方法，这样您就可以避免由于数据分布或索引变化而选择较慢的索引。过滤器具有以下属性：
+ **无需更改应用程序。**过滤器使用数据库命令设置并应用到服务器上，因此无需部署代码即可缓解回归。这是与 a 的主要区别`hint`，后者必须在每个呼叫站点添加。
+ **范围是一个查询形状。**形状由集合、谓词和排序定义，而不是由完整查询定义，因此过滤器仅限制与该形状匹配的查询。对同一馆藏的其他查询仍在正常计划之中。
+ **这种变化是持久的。**过滤器在实例重启和引擎修补后仍然有效。删除该集合会移除其过滤器。
+ **这种变化是可逆的。**清除过滤器会使形状恢复到基于成本的正常计划，因此过滤器是一种在调查根本原因时稳定工作负载的低风险方法。

**Topics**
+ [支持的 命令](#plan-cache-filter-support)
+ [支持的查询形状](#plan-cache-filter-shapes)
+ [设置、列出和清除筛选器](#plan-cache-filter-api)
+ [验证是否应用了过滤器](#plan-cache-filter-explain)
+ [示例](#plan-cache-filter-examples)
+ [带有提示和索引更改的行为](#plan-cache-filter-behavior)

## 支持的 命令
<a name="plan-cache-filter-support"></a>

计划缓存过滤器需要计划器版本 2.0 或更高版本。筛选条件可以应用的命令取决于计划器版本和 Amazon DocumentDB 次要版本，如下表所示。


**对计划缓存过滤器的命令支持**  

| 命令 | 最低计划器版本 | 最低次要版本 | 
| --- | --- | --- | 
| `find` | 2.0 | 5.0.0、8.0.0 | 
| `count` | 2.0 | 5.0.0、8.0.0 | 
| `update` | 2.0 | 5.0.2、8.0.2 | 
| `delete` | 2.0 | 5.0.2、8.0.2 | 
| `findAndModify` | 2.0 | 5.0.2、8.0.2 | 
| `distinct` | 3.0 | 8.0.0 | 
| `aggregate` | 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`，筛选器适用于`count``update`、`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`阶段的外部集合上设置的过滤器驱动用于扫描该外国集合的索引。

## 支持的查询形状
<a name="plan-cache-filter-shapes"></a>

从 Amazon DocumentDB 5.0.2 和 8.0.2 开始，以下运算符可以出现在具有计划缓存筛选器的查询形状中：
+ `$text`。搜索字符串是标准化的，因此仅在搜索词上不同的查询共享一个形状。
+ `$near` 和 `$nearSphere` 两个运算符均归一化为相同的形状。坐标`$minDistance`、和`$maxDistance`不是形状的一部分。
+ `$geoWithin` 和 `$geoIntersects` GeoJSON 几何类型是形状的一部分，因此`Polygon`查询和`MultiPolygon`查询是不同的形状。坐标不是形状的一部分。
+ `$regex`，直接在球场上使用时。模式和选项已标准化，因此同一字段上的所有正则表达式共享一个形状。

为使用`$jsonSchema`、`$expr`或`$sampleRate`的查询形状设置过滤器失败，错误代码为 303。对于以下形状，它也失败了。使用其中任何一种的查询都是在计划中不使用过滤器的：
+ `$regex`嵌套在里面`$in``$nin`、或`$all`。将正则表达式与文字值混合在一起的数组无法根据索引进行评估，因此永远无法对该形状应用过滤器。
+ 的排序规范`{$natural: 1}`。`$natural`排序请求集合扫描，这与将计划器限制为索引不兼容。

## 设置、列出和清除筛选器
<a name="plan-cache-filter-api"></a>

要设置计划缓存筛选器，请使用`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: {}})
```

## 验证是否应用了过滤器
<a name="plan-cache-filter-explain"></a>

`explain`输出包括两个报告计划缓存过滤器状态的字段：
+ `indexFilterSet`是`true`当集合上存在查询形状的过滤器时。
+ `indexFilterApplied``true`仅当使用该过滤器列表中的索引计划查询时。

从 Amazon DocumentDB 5.0.2 和 8.0.2 开始，每条支持计划缓存筛选器的命令都会报告这些字段，并在这些命令的分析器输出中报告这些字段。在早期的次要版本中，它们仅针对`find`命令进行了报告。有关阅读 explain 输出的更多信息，请参阅[查询计划分析](performance-query-plan-analysis.md)。

过滤器无需应用即可匹配查询形状。如果过滤器列表中的所有索引都无法为查询提供服务，则计划者在没有过滤器的情况下重新规划，并选择成本指数。在这种情况下，`indexFilterSet`是`true`、`indexFilterApplied`是`false`。当命名索引不覆盖查询的字段、命名索引不存在或`$regex`形状无法生成索引边界（例如未锚定或不区分大小写的模式）时，就会发生这种情况。

## 示例
<a name="plan-cache-filter-examples"></a>

以下示例使用`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
```

### 示例：将`查找`查询固定到复合索引
<a name="plan-cache-filter-example-find"></a>

假设一个按以下条件进行过滤`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`。

### 示例：`将`更新固定到选择性索引
<a name="plan-cache-filter-example-update"></a>

更新必须先找到要修改的文档，然后才能写入它们，并且该查询使用索引的方式与读取相同。当一个谓词可以由多个索引提供服务时，规划者选择的索引决定更新检查的文档数量。

```
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 外国`集合
<a name="plan-cache-filter-example-aggregate"></a>

对于聚合，形状取自前一`$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。

## 带有提示和索引更改的行为
<a name="plan-cache-filter-behavior"></a>
+ 计划缓存过滤器的优先级高于`hint`。Amazon DocumentDB 首先应用过滤器，然后在过滤器的允许索引列表中应用提示。如果提示命名了过滤器允许的索引，则使用该索引。如果提示命名了过滤器不允许的索引，则该提示将被忽略，计划者根据成本在允许的索引中进行选择。无论是命名索引还是给出其键模式，这两种提示表单的行为方式都相同。

  例如，如果集合`foo`的索引为`a_1``b_1``a_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`。重新创建同名索引使其再次符合条件。
+ 删除一个集合会移除该集合的所有过滤器。