

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

# Amazon DocumentDB 故障排除
<a name="troubleshooting"></a>

以下部分提供了有关如何排查您在使用 Amazon DocumentDB（与 MongoDB 兼容） 时可能遇到的问题的信息。

**Topics**
+ [连接问题](troubleshooting.connecting.md)
+ [索引](troubleshooting.index-creation.md)
+ [性能和资源利用率](user_diagnostics.md)
+ [垃圾回收](garbage-collection.md)

# 排除连接问题
<a name="troubleshooting.connecting"></a>

连接遇到问题？ 以下是一些常见场景及如何解决。

**主题**
+ [无法连接到 Amazon DocumentDB 端点](#troubleshooting-connecting)
+ [测试与 Amazon DocumentDB 实例的连接](#troubleshooting.testing-connection)
+ [连接到无效终端节点](#troubleshooting.invalid-endpoint)
+ [影响连接数的驱动程序配置](#troubleshooting.driver.config)

## 无法连接到 Amazon DocumentDB 端点
<a name="troubleshooting-connecting"></a>

当您尝试连接到 Amazon DocumentDB 时，以下错误消息是您可能收到的最常见的错误消息之一。

```
connecting to: mongodb://docdb-2018-11-08-21-47-27.cluster-ccuszbx3pn5e.us-east-
1.docdb.amazonaws.com:27017/
2018-11-14T14:33:46.451-0800 W NETWORK [thread1] Failed to connect to
172.31.91.193:27017 after 5000ms milliseconds, giving up.
2018-11-14T14:33:46.452-0800 E QUERY [thread1] Error: couldn't connect to server
docdb-2018-11-08-21-47-27.cluster-ccuszbx3pn5e.us-east-1.docdb.amazonaws.com:27017,
connection attempt failed :
connect@src/mongo/shell/mongo.js:237:13
@(connect):1:6
exception: connect failed
```

此错误消息通常意味着您的客户端（此示例中为 mongo Shell）无法访问 Amazon DocumentDB 端点。以下几个原因可能会导致出现此情况：

**Topics**
+ [从公有端点连接](#troubleshooting.cannot-connect.public-endpoints)
+ [跨区域连接](#troubleshooting.cannot-connect.different-regions)
+ [从不同的 Amazon 连接 VPCs](#troubleshooting.cannot-connect.different-vpcs)
+ [安全组阻止入站连接](#troubleshooting.cannot-connect.inbound-not-allowed)
+ [Java Mongo 驱动程序读取首选项问题](#troubleshooting-cannot-connect-java-mongo-issue)

### 从公有端点连接
<a name="troubleshooting.cannot-connect.public-endpoints"></a>

**您正在尝试直接通过笔记本电脑或本地开发计算机连接到 Amazon DocumentDB 集群。**

从公有端点（例如，您的笔记本电脑或本地开发机）直接连接到 Amazon DocumentDB 集群的尝试会失败。Amazon DocumentDB 仅面向虚拟私有云（VPC），并且当前不支持公共端点。因此，您无法从笔记本电脑或 VPC 外部的本地开发环境直接连接到 Amazon DocumentDB 集群。

要从 Amazon VPC 外部连接到 Amazon DocumentDB 集群，您可使用 SSH 隧道。有关更多信息，请参阅 [从 Amazon VPC 外部连接到 Amazon DocumentDB 集群](connect-from-outside-a-vpc.md)。此外，如果您的开发环境位于不同的 Amazon VPC 中，您还可以使用 VPC 对等，并从同一区域或不同区域中的另一个 Amazon VPC 连接到您的 Amazon DocumentDB 集群。

### 跨区域连接
<a name="troubleshooting.cannot-connect.different-regions"></a>

**您正在尝试连接到另一个区域中的 Amazon DocumentDB 集群。**

如果您尝试从集群区域以外某区域的 Amazon EC2 实例连接到 Amazon DocumentDB 集群，例如，尝试从美国西部（俄勒冈州）区域 (us-west-2) 连接到美国东部（弗吉尼亚州北部）区域 (us-east-1) 中的集群，连接将失败。

要验证您的 Amazon DocumentDB 集群的区域，请运行以下命令。区域位于终端节点中。

```
aws docdb describe-db-clusters \
   --db-cluster-identifier sample-cluster \
   --query 'DBClusters[*].Endpoint'
```

此操作的输出将类似于下文。

```
[
    "sample-cluster.node.us-east-1.docdb.amazonaws.com"
]
```

要验证您的 EC2 实例的区域，请运行以下命令。

```
 aws ec2 describe-instances \
     --query 'Reservations[*].Instances[*].Placement.AvailabilityZone'
```

此操作的输出将类似于下文。

```
[
    [
        "us-east-1a"
    ]
]
```

### 从不同的 Amazon 连接 VPCs
<a name="troubleshooting.cannot-connect.different-vpcs"></a>

**您正在尝试通过一个 VPC（不同于您的集群部署到的 Amazon VPC）连接到 Amazon DocumentDB 集群。**

如果您的 Amazon DocumentDB 集群和 Amazon EC2 实例位于相同但不在同一个亚马逊 VPC 中 AWS 区域，则除非在两个亚马逊之间启用 VPC 对等互连，否则您无法直接连接到您的 Amazon DocumentDB 集群。 VPCs

要验证 Amazon DocumentDB 实例的 Amazon VPC，请运行以下命令。

```
aws docdb describe-db-instances \
   --db-instance-identifier sample-instance \
   --query 'DBInstances[*].DBSubnetGroup.VpcId'
```

要验证您的 Amazon EC2 实例的 Amazon VPC，请运行以下命令。

```
aws ec2 describe-instances \
   --query 'Reservations[*].Instances[*].VpcId'
```

### 安全组阻止入站连接
<a name="troubleshooting.cannot-connect.inbound-not-allowed"></a>

**您正在尝试连接到 Amazon DocumentDB 集群，并且该集群的安全组不允许集群端口（默认端口：27017）上的入站连接。**

假设您的 Amazon DocumentDB 集群和 Amazon EC2 实例位于同一区域和 Amazon VPC 中，并且使用相同的 Amazon VPC 安全组。如果您无法连接到您的 Amazon DocumentDB 集群，这可能是因为您的集群的安全组（即防火墙）不允许您为 Amazon DocumentDB 集群选择的端口（默认端口为 27017）上的入站连接。

要验证您的 Amazon DocumentDB 集群的端口，请运行以下命令。

```
aws docdb describe-db-clusters \
   --db-cluster-identifier sample-cluster \
   --query 'DBClusters[*].[DBClusterIdentifier,Port]'
```

要获取您的集群的 Amazon DocumentDB 安全组，请运行以下命令。

```
aws docdb describe-db-clusters \
   --db-cluster-identifier sample-cluster \
   --query 'DBClusters[*].[VpcSecurityGroups[*],VpcSecurityGroupId]'
```

要检查您的安全组的入站规则，请参阅 Amazon EC2 文档中的以下主题：
+ [为您的 Linux 实例授权入站流量](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html)
+ [为您的 Windows 实例授权入站流量](https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/authorizing-access-to-an-instance.html)

### Java Mongo 驱动程序读取首选项问题
<a name="troubleshooting-cannot-connect-java-mongo-issue"></a>

**客户端读取首选项未被接受，并且除非重启，某些客户端无法在失效转移后写入 Amazon DocumentDB。**

这个问题最初在 Java Mongo Driver 3.7.x 中发现，当客户端使用 `MongoClientSettings` 与 Amazon DocumentDB 建立连接时，尤其链接 `applyToClusterSettings` 方法时，会发生。可以使用几种不同的方法来定义 MongoClient 集群设置，例如`hosts()``requiredReplicaSetName()`、和`mode()`。

 当客户端在 `hosts()` 方法中仅指定一台主机时，模式将设置为 `ClusterConnectionMode.SINGLE` 而非 `ClusterConnectionMode.MULTIPLE`。这导致客户端忽略读取首选项而仅连接到按 `hosts()` 配置的服务器。因此，即使客户端设置如下所示初始化，所有读取仍将转至主服务器而不是辅助服务器。

```
final ServerAddress serverAddress0 = new ServerAddress("cluster-endpoint", 27317));
    final MongoCredential credential = MongoCredential.createCredential("xxx",
            "admin", "xxxx".toCharArray());
    final MongoClientSettings settings = MongoClientSettings.builder()
            .credential(credential)
            .readPreference(ReadPreference.secondaryPreferred())
            .retryWrites(false)
            .applyToSslSettings(builder -> builder
                    .enabled(false))
            .applyToClusterSettings(builder -> builder.hosts(
                            Arrays.asList(serverAddress0
                            ))
                    .requiredReplicaSetName("rs0"))
            .build();
    MongoClient mongoClient = MongoClients.create(settings);
```

**失效转移案例**

使用上述的客户端连接设置，如果集群写入器端点存在失效转移和延迟的 DNS 记录更新，则客户端仍尝试向旧写入器（失效转移后现在是读取器）发出写入请求。这导致 Java 驱动程序未恰当处理的服务器端错误（不是主错误）（这仍在调查中）。因此，可能听任客户端处于不良状态，例如直到应用程序服务器重新启动为止。

对此有两种应变方法：
+ 通过连接字符串连接到 Amazon DocumentDB 的客户端将不会出现这个问题，因为设置读取首选项时`ClusterConnectionMode` 会设置成 `MULTIPLE`。

  ```
  MongoClientURI mongoClientURI = new MongoClientURI("mongodb://usr:pass:cluster-endpoint:27317/test?ssl=false&replicaSet=rs0&readpreference=secondaryPreferred");
  MongoClient mongoClient = MongoClients.create(mongoClientURI.getURI());
  ```

  或者配合 `applyConnectionString` 方法使用 `MongoClientSettings` 生成器。

  ```
  final MongoClientSettings settings = MongoClientSettings.builder()
          .credential(credential)
          .applyConnectionString(new ConnectionString("usr:pass:cluster-endpoint:27317/test?ssl=false&replicaSet=rs0&readpreference=secondaryPreferred"))
          .retryWrites(false)
          .applyToSslSettings(builder → builder
                  .enabled(false))
          .build();
  MongoClient mongoClient = MongoClients.create(settings);
  ```
+ 将 `ClusterConnectionMode` 明确设置成 `MULTIPLE`。只有使用 `applyToClusterSettings` 和 `hosts().size() == 1` 时才需要这样做。

  ```
  final ServerAddress serverAddress0 = new ServerAddress("cluster-endpoint", 27317));
  final MongoCredential credential = MongoCredential.createCredential("xxx","admin", "xxxx".toCharArray());
  final MongoClientSettings settings = MongoClientSettings.builder()
      .credential(credential)
      .readPreference(ReadPreference.secondaryPreferred())
      .retryWrites(false)
      .applyToSslSettings(builder → builder
      .enabled(false))
      .applyToClusterSettings(builder → builder
                  .hosts(Arrays.asList(serverAddress0))
                  .requiredReplicaSetName("rs0"))
                  .mode(ClusterConnectionMode.MULTIPLE))
      .build();
  MongoClient mongoClient = MongoClients.create(settings);
  ```

## 测试与 Amazon DocumentDB 实例的连接
<a name="troubleshooting.testing-connection"></a>

您可以使用常见的 Linux 或 Windows 工具测试与集群的连接。

 从 Linux 或 Unix 终端测试此连接，方法为输入以下命令（将 `cluster-endpoint` 替换为终端节点并将 `port` 替换为实例的端口）。

```
nc -zv cluster-endpoint port 
```

以下是示例操作和返回值的示例：

```
nc -zv docdbTest.d4c7nm7stsfc0.us-west-2.docdb.amazonaws.com 27017
   
Connection to docdbTest.d4c7nm7stsfc0.us-west-2.docdb.amazonaws.com 27017 port [tcp/*] succeeded!
```

## 连接到无效终端节点
<a name="troubleshooting.invalid-endpoint"></a>

在连接到 Amazon DocumentDB 集群并且使用的是无效的集群端点时，将出现与以下内容类似的错误。

```
mongo --ssl \
   --host sample-cluster.node.us-east-1.docdb.amazonaws.com:27017 \
   --sslCAFile global-bundle.pem \
   --username <user-name> \
   --password <password>
```

该输出类似于以下示例：

```
MongoDB shell version v3.6
connecting to: mongodb://sample-cluster.node.us-east-1.docdb.amazonaws.com:27017/
2018-11-14T17:21:18.516-0800 I NETWORK [thread1] getaddrinfo("sample-cluster.node.us-east-1.docdb.amazonaws.com") failed: 
nodename nor servname provided, or not known 2018-11-14T17:21:18.537-0800 E QUERY [thread1] Error: couldn't initialize
connection to host sample-cluster.node.us-east-1.docdb.amazonaws.com, address is invalid :
connect@src/mongo/shell/mongo.js:237:13@(connect):1:6
exception: connect failed
```

要获取集群的有效终端节点，请运行以下命令：

```
aws docdb describe-db-clusters \
   --db-cluster-identifier sample-cluster \
   --query 'DBClusters[*].[Endpoint,Port]'
```

要获取实例的有效终端节点，请运行以下命令：

```
aws docdb describe-db-instances \
   --db-instance-identifier sample-instance \
   --query 'DBInstances[*].[Endpoint.Address,Endpoint.Port]'
```

有关更多信息，请参阅 [了解 Amazon DocumentDB 端点](endpoints.md)。

## 影响连接数的驱动程序配置
<a name="troubleshooting.driver.config"></a>

使用客户端驱动程序连接到 Amazon DocumentDB 集群时，请务必考虑 `maxPoolSize` 配置参数。`maxPoolSize` 设置决定了客户端驱动程序可以在其连接池中保持的最大连接数。

# 索引故障排除
<a name="troubleshooting.index-creation"></a>

以下主题说明了索引或后台索引构建失败时的操作。

**Topics**
+ [索引构建失败](#troubleshooting.index-build-fails)
+ [后台索引构建延迟问题和失败](#troubleshooting.background-index-build-fails)
+ [数据库索引膨胀](#troubleshooting-database-bloat)

## 索引构建失败
<a name="troubleshooting.index-build-fails"></a>

在索引创建过程中，Amazon DocumentDB 将使用实例上的本地存储。您可以使用**FreeLocalStorage** CloudWatch 指标 (`CloudWatch -> Metrics -> DocDB -> Instance Metrics`) 监控此磁盘使用情况。如果索引构建用尽所有本地磁盘并且失败，您将收到错误。将数据迁移到 Amazon DocumentDB 时，我们建议您先创建索引，然后插入数据。有关迁移策略和创建索引的更多信息，请参阅 Amazon DocumentDB 文档中的 [迁移到 Amazon DocumentDB](docdb-migration.md) 和博客文章：[使用离线方法从 MongoDB 迁移到 Amazon DocumentDB](https://aws.amazon.com/blogs/database/migrate-from-mongodb-to-amazon-documentdb-using-the-offline-method/)。

在现有集群上创建索引时，如果索引构建花费的时间超过预期或构建失败，我们建议您扩展实例来创建索引，创建好索引后，再缩减回来。Amazon DocumentDB 使您能够使用 AWS 管理控制台 或在几分钟内快速扩展实例大小。 AWS CLI有关更多信息，请参阅 [管理实例类](db-instance-classes.md)。使用每秒实例定价时，您只需为使用 1 秒以上的资源付费。

## 后台索引构建延迟问题和失败
<a name="troubleshooting.background-index-build-fails"></a>

Amazon DocumentDB 中后台索引构建直到索引构建发起之前启动的主实例上所有查询都完成执行后才启动。如果有长时间运行的查询，则后台索引构建将阻塞，直到查询完成为止，因此可能耗时比预期完成时间更长。即使集合为空也如此。

前台索引构建并不展现出相同的阻塞行为。取而代之，前台索引构建对集合进行独占锁定，直至完成索引构建为止。因此，要在空集合上创建索引并避免在任何长时间运行的查询上阻塞，我们建议使用前台索引构建。

**注意**  
在任何给定时间，Amazon DocumentDB 只允许在一个集合中构建一个后台索引。如果后台索引构建期间在相同集合上出现 DDL（数据定义语言）操作（如 `createIndex()` 或 `dropIndex()`），后台索引构建将失败。

## 数据库索引膨胀
<a name="troubleshooting-database-bloat"></a>

Amazon DocumentDB 使用多版本并发控制（MVCC）来管理并发事务。删除或更新文档后，其先前版本将作为“失效”版本保留在集合和索引中。垃圾回收过程会自动从这些失效版本中回收空间以用于未来的操作。

当集合的索引因失效或过时的索引条目累积或页面内的碎片累积而变大时，就会出现索引膨胀。报告的百分比表示未来索引条目可以使用的索引空间量。此膨胀会同时消耗缓冲区缓存和存储空间中的空间。如果要消除膨胀，则需要重建索引。

**Example 示例**  
运行以下命令以确定索引未使用的存储空间：  

```
db.coll.aggregate({$indexStats:{}});
```
结果类似如下：  

```
{ 
    "name" : "_id_",
    "key" : { 
        "_id" : 1 
    },
    "host" : "devbox-test.localhost.a2z.com:27317",
    "size" : NumberLong(827392),
    "accesses" : {
        "ops" : NumberLong(40000),
        "docsRead" : NumberLong(46049),
        "since" : ISODate("2025-04-03T21:44:51.251Z") 
    },
    "cacheStats" : {
        "blksRead" : NumberLong(264),
        "blksHit" : NumberLong(140190),
        "hitRatio" : 99.8121
    }, 
    "unusedStorageSize" : {
        "unusedSizeBytes" : 409600,
        "unusedSizePercent" : 49.51
    }
}
```

使用 `reIndex` 命令无需停机即可重建索引，该命令需要扫描整个集合。请参阅[使用 `reIndex` 进行索引维护](managing-indexes.md#reIndex)。

# 性能和资源利用率故障排除
<a name="user_diagnostics"></a>

本部分提供了 Amazon DocumentDB 部署中的常见诊断问题和解决方案。提供的示例使用 *mongo Shell*，并限制在单个实例范围内。要查找实例终端节点，请参阅[了解 Amazon DocumentDB 端点](endpoints.md)。

**Topics**
+ [如何确定通过 Mongo API 对我的集合执行的插入、更新和删除操作的次数？](#user-diag-performed-operations)
+ [如何分析缓存性能？](#user-diag-cache-perf)
+ [如何查找和终止长时间运行或受阻的查询？](#user_diagnostics-query_terminating)
+ [如何查看查询计划和优化查询？](#user_diagnostics-query_plan)
+ [如何在弹性集群中查看查询计划？](#user-diagnostics-ec-query-plan)
+ [如何列出实例上所有正在运行的操作？](#user_diagnostics-list_queries)
+ [如何知道查询何时取得进展？](#user_diagnostics-query_progressing)
+ [如何确定系统突然运行缓慢的原因？](#user_diagnostics-speed_change)
+ [如何确定一个或多个集群实例上 CPU 使用率过高的原因？](#user_diagnostics-cpu_utilization)
+ [如何确定实例上打开的光标？](#user_diagnostics-open_cursors)
+ [如何确定当前的 Amazon DocumentDB 引擎版本？](#user_diagnostics-engine_version)
+ [如何分析索引使用情况并识别未使用的索引？](#user-diag-index-usage)
+ [如何识别缺失的索引？](#user_diagnostics-identify_missing_indexes)
+ [如何确定数据库集合膨胀？](#performance-collection-bloat)
+ [有用查询的摘要](#user_diagnostics-useful_queries)

## 如何确定通过 Mongo API 对我的集合执行的插入、更新和删除操作的次数？
<a name="user-diag-performed-operations"></a>

要查看对某个集合执行的插入、更新和删除操作的数量，请对该集合运行以下命令：

```
db.collection.stats()
```

此命令的输出在其 `opCounters` 字段下描述了以下内容：
+ **numDocsIns**-插入到此集合中的文档数量。这包括使用 `insert` 和 `insertMany` 命令插入的文档，以及通过更新插入的文档。
+ **numDocsUpd**-此集合中更新的文档数量。这包括使用 `update` 和 `findAndModify` 命令更新的文档。
+ **numDocsDel**-从该集合中删除的文档数量。这包括使用`deleteOne`、`deleteMany`、`remove`、和 `findAndModify` 命令删除的文档。
+ **lastReset**：上次重置这些计数器的时间。当集群或扩展 up/down 实例时 starting/stopping ，此命令提供的统计数据将被重置。

运行 `db.collection.stats()` 的输出示例如下。

```
{
    "ns" : "db.test",
    "count" : ...,
    "size" : ...,
    "avgObjSize" : ...,
    "storageSize" : ...,
    "capped" : false,
    "nindexes" : ...,
    "totalIndexSize" : ...,
    "indexSizes" : {
        "_id_" : ...,
        "x_1" : ...
    },
    "collScans" : ...,
    "idxScans" : ...,
    "opCounter" : {
        "numDocsIns" : ...,
        "numDocsUpd" : ...,
        "numDocsDel" : ...
    },
    "cacheStats" : {
        "collBlksHit" : ...,
        "collBlksRead" : ..,
        "collHitRatio" : ...,
        "idxBlksHit" : ...,
        "idxBlksRead" : ...,
        "idxHitRatio" : ...
    },
    "lastReset" : "2022-09-02 19:41:40.471473+00",
    "ok" : 1,
    "operationTime" : Timestamp(1662159707, 1)
}
```

通过 Mongo API 查看用于插入、更新和删除操作的集合专用计数器时，应使用此 stats 命令。另一种查看特定于集合的操作计数器的方法是启用 DML 审核。可以在 [使用以下方式监控亚马逊 DocumentDB CloudWatch](cloud_watch.md) 中查看在一分钟时间间隔内对所有集合执行的插入、更新和删除操作的次数。

## 如何分析缓存性能？
<a name="user-diag-cache-perf"></a>

分析缓存性能可以深入了解数据检索的效率和系统性能，并且基于从磁盘读取的数据量与从缓存中读取的数据量。我们提供有关缓存命中次数（从缓存中读取的数据）和缓存未命中（在缓存中找不到并从磁盘读取的数据）的缓存统计信息，以便深入了解缓存性能。通过对该集合运行以下命令可以找到特定集合的缓存统计信息：

```
db.collection.stats()
```

此命令输出中 `cacheStats` 字段中的值提供集合的缓存统计信息，以及在集合上创建的索引的缓存总统计信息。以下列出了这些统计数据：
+ **`collBlksHit`**：操作此集合期间从缓存中读取的块数。
+ **`collBlksRead`**：操作此集合期间从磁盘读取的块数（缓存未命中）。
+ **`collHitRatio`**：此集合的缓存命中率 (`100 * [collBlksHit / (collBlksHit + collBlksRead)]`)。
+ **`idxBlksHit`**：从缓存中读取在此集合上创建的任何索引的块数。
+ **`idxBlksRead`**：在此集合上创建的任何索引从磁盘读取的块数（缓存未命中）。
+ **`idxHitRatio`**：在此集合上创建的索引的缓存命中率 (`100 * [idxBlksHit / (idxBlksHit + idxBlksRead)]`)。
+ **`lastReset`**：上次重置这些统计数据的时间。集群或扩展 up/down 实例时 starting/stopping ，提供的统计数据会`db.collection.stats()`被重置。

也可以使用 `indexStats` 命令查看每个索引的 `idxBlksHit` 和 `idxBlksRead` 字段的细分。运行以下命令可以找到特定于索引的缓存统计信息：

```
db.collection.aggregate([{$indexStats:{}}]).pretty()
```

对于每个索引，可以在 `cacheStats` 字段下找到以下缓存统计信息：
+ **`blksHit`**：从缓存中读取此索引的数据块的数量。
+ **`blksRead`**：从磁盘中读取此索引的数据块的数量。
+ **`blksHitRatio`**：缓存命中率四舍五入到小数点后四位，计算公式为 `100 * [blksHit / (blksHit + blksRead)]`。

## 如何查找和终止长时间运行或受阻的查询？
<a name="user_diagnostics-query_terminating"></a>

用户查询可能因查询计划不够理想而运行缓慢，或者由于资源争用而受阻。

要查找因查询计划不够理想而速度缓慢的长时间运行的查询，或者由于资源争用而受阻的查询，请使用 `currentOp` 命令。可以筛选该命令以帮助缩小要终止的相关查询的列表。长时间运行的查询必须拥有关联的 `opid`，才能够终止查询。

以下查询使用 `currentOp` 命令列出受阻或运行时间超过 10 秒的所有查询。

```
db.adminCommand({
    aggregate: 1,
    pipeline: [
        {$currentOp: {}},
        {$match: 
            {$or: [
                {secs_running: {$gt: 10}},
                {WaitState: {$exists: true}}]}},
        {$project: {_id:0, opid: 1, secs_running: 1}}],
    cursor: {}
});
```

接下来，您可以缩小查询，以查找运行时间超过 10 秒的查询的 `opid` 并终止它。

**查找并终止运行时间超过 10 秒的查询**

1. 查找查询的 `opid`。

   ```
   db.adminCommand({
       aggregate: 1,
       pipeline: [
           {$currentOp: {}},
           {$match: 
               {$or: 
                   [{secs_running: {$gt: 10}},
                    {WaitState: {$exists: true}}]}}],
       cursor: {}
   });
   ```

   此操作的输出将类似于下文（JSON 格式）。

   ```
   {
       "waitedMS" : NumberLong(0),
       "cursor" : {
           "firstBatch" : [
               {
                   "opid" : 24646,
                   "secs_running" : 12 
               }
           ],
           "id" : NumberLong(0),
           "ns" : "admin.$cmd"
       },
       "ok" : 1
   }
   ```

1. 使用 `killOp` 操作终止查询。

   ```
   db.adminCommand({killOp: 1, op: 24646});
   ```

## 如何查看查询计划和优化查询？
<a name="user_diagnostics-query_plan"></a>

如果查询运行缓慢，可能是因为查询执行需要对集合进行完全扫描以选择相关的文档。有时，可通过创建合适的索引提高查询的运行速度。要检测此情形并确定要在其中创建索引的字段，请使用 `explain` 命令。

**注意**  
Amazon DocumentDB 在利用分布式、容错、自修复的存储系统的专用数据库引擎上模拟 MongoDB 3.6 API。因此，查询计划和 `explain()` 的输出在 Amazon DocumentDB 和 MongoDB 之间可能有所不同。希望控制其查询计划的客户可以使用 `$hint` 运算符强制选择首选索引。

在 `explain` 命令下运行要改进的查询，如下所示。

```
db.runCommand({explain: {<query document>}})
```

以下是操作示例。

```
db.runCommand({explain:{
    aggregate: "sample-document",
    pipeline: [{$match: {x: {$eq: 1}}}],
    cursor: {batchSize: 1}}
});
```

此操作的输出将类似于下文（JSON 格式）。

```
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "db.test",
        "winningPlan" : {
            "stage" : "COLLSCAN"
        }
    },
    "serverInfo" : {
        "host" : "...",
        "port" : ...,
        "version" : "..."
    },
    "ok" : 1
}
```

上述输出表明，`$match` 阶段要求扫描整个集合并检查每个文档中的字段 `"x"` 是否等于 1。如果集合中有很多文档，集合扫描将非常慢，因此整体查询性能非常低。因此，`explain` 命令输出中的 `"COLLSCAN"` 的存在表明，可以通过创建合适的索引来提高查询性能。

在本例中，查询检查所有文档中的字段 `"x"` 是否等于 1。因此，在字段 `"x"` 上创建索引，可使查询避免对集合进行完全扫描，并可使用索引更快地返回相关文档。

在字段 `"x"` 上创建索引后，`explain` 输出如下所示。

```
{
    "queryPlanner" : {
         "plannerVersion" : 1,
         "namespace" : "db.test",
         "winningPlan" : {
             "stage" : "IXSCAN",
             "indexName" : "x_1",
             "direction" : "forward"
         }
    },
    "serverInfo" : {
        "host" : "...",
        "port" : ...,
        "version" : "..."
    },
    "ok" : 1
}
```

因此，在 `"x"` 字段上创建索引后，`$match` 阶段即可使用索引扫描来减少必须对其评估 `"x = 1"` 谓词的文档的数量。

对于较小的集合，如果性能增益微乎其微，Amazon DocumentDB 查询处理器可以选择不使用索引。

## 如何在弹性集群中查看查询计划？
<a name="user-diagnostics-ec-query-plan"></a>

要检查弹性集群中的查询计划，请使用 `explain` 命令。以下是针对分片集合的查找查询的 `explain` 操作示例：

```
db.runCommand(
   {
     explain: { find: "cities", filter: {"name": "Seoul"}}
   }
)
```

**注意**  
Amazon DocumentDB 在专用数据库引擎上模拟 MongoDB。因此，查询计划和 `explain()` 的输出在 Amazon DocumentDB 和 MongoDB 之间可能有所不同。您可以使用 `$hint` 运算符强制选择首选索引来控制查询计划。

此操作的输出将可能类似于下文（JSON 格式）。

```
{
  "queryPlanner" : {
    "elasticPlannerVersion" : 1,
    "winningPlan" : {
      "stage" : "SINGLE_SHARD",
      "shards" : [
        {
          "plannerVersion" : 1,
          "namespace" : "population.cities",
          "winningPlan" : {
            "stage" : "SHARD_MERGE",
            "shards" : [
              {
                "shardName" : "f2cf5cfd-fe9c-40ca-b4e5-298ca0d11111",
                "plannerVersion" : 1,
                "namespace" : "population.cities",
                "winningPlan" : {
                  "stage" : "PARTITION_MERGE",
                  "inputStages" : [
                    {
                      "stage" : "COLLSCAN",
                      "partitionCount" : 21
                    }
                  ]
                }
              },
              {
                "shardName" : "8f3f80e2-f96c-446e-8e9d-aab8c7f22222",
                "plannerVersion" : 1,
                "namespace" : "population.cities",
                "winningPlan" : {
                  "stage" : "PARTITION_MERGE",
                  "inputStages" : [
                    {
                      "stage" : "COLLSCAN",
                      "partitionCount" : 21
                    }
                  ]
                }
              },
              {
                "shardName" : "32c5a06f-1b2b-4af1-8849-d7c4a033333",
                "plannerVersion" : 1,
                "namespace" : "population.cities",
                "winningPlan" : {
                  "stage" : "PARTITION_MERGE",
                  "inputStages" : [
                    {
                      "stage" : "COLLSCAN",
                      "partitionCount" : 22
                    }
                  ]
                }
              }
            ]
          },
          "shardName" : "32c5a06f-1b2b-4af1-8849-d7c4a0f3fb58"
        }
      ]
    }
  },
  "serverInfo" : {
    "host" : "example-4788267630.us-east-1.docdb-elastic.amazonaws.com:27017",
    "version" : "5.0.0"
  },
  "ok" : 1,
  "operationTime" : Timestamp(1695097923, 1)
}
```

前面的输出显示了三分片集群上 `find` 查询的查询计划。每个分片都有多个数据分区，这些分区可以有不同的输入阶段。在此示例中，在每个分片的“PARTITION\$1MERGE”阶段合并结果之前，将在所有分区上运行“COLLSCAN”（集合扫描）。然后，分片上的结果在“SHARD\$1MERGE”阶段合并在一起，再发送回客户端。

## 如何列出实例上所有正在运行的操作？
<a name="user_diagnostics-list_queries"></a>

作为用户或主用户，您经常需要列出实例上当前正在运行的所有操作，以进行诊断和故障排除。（有关管理用户的信息，请参阅[管理 Amazon DocumentDB 用户](security.managing-users.md)）。

借助 `mongo` Shell，您可以使用以下查询列出 Amazon DocumentDB 实例上正在运行的所有操作。

```
db.adminCommand({currentOp: 1, $all: 1});
```

该查询返回当前在实例上运行的所有用户查询和内部系统任务的完整列表。

此操作的输出将类似于下文（JSON 格式）。

```
{
    "inprog" : [
        {
            "desc" : "INTERNAL"
        },
        {
            "desc" : "TTLMonitor",
            "active" : false
        },
        {
            "client" : ...,
            "desc" : "Conn",
            "active" : true,
            "killPending" : false,
            "opid" : 195,
            "ns" : "admin.$cmd",
            "command" : {
                "currentOp" : 1,
                "$all" : 1
            },
            "op" : "command",
            "$db" : "admin",
            "secs_running" : 0,
            "microsecs_running" : NumberLong(68),
            "clientMetaData" : {
            "application" : {
                "name" : "MongoDB Shell"
            },
            "driver" : {
                ...
            },
            "os" : {
                ...
            }
          }
       },
       {
          "desc": "GARBAGE_COLLECTION",
          "garbageCollection": {
             "databaseName": "testdb",
             "collectionName": "testCollectionA"
          },
          "secs_running": 3,
          "microsecs_running": NumberLong(3123456)
       },
       {
          "desc": "GARBAGE_COLLECTION",
          "garbageCollection": {
             "databaseName": "testdb",
             "collectionName": "testCollectionB"
          },
          "secs_running": 4,
          "microsecs_running": NumberLong(4123456)
       }
    ],
    "ok" : 1
}
```

`"desc"` 字段的有效值如下所示：
+ **INTERNAL**：内部系统任务，如游标清理或过时用户清理任务。
+ **TTLMonitor**：生存时间 (TTL) 监视器线程。其运行状态在 `"active"` 字段中反映。
+ **GARBAGE\$1COLLECTION**：内部垃圾收集器线程。
+ **CONN**：用户查询。
+ **CURSOR**：该操作是一个空闲的光标，等待用户调用“getMore”命令以获取下一批结果。在此状态下，光标正在消耗内存，但不消耗任何计算。

上述输出还列出了在系统中运行的所有用户查询。每个用户查询都在数据库和集合的上下文中运行，而这二者的并集称为*命名空间*。每个用户查询的命名空间都可在 `"ns"` 字段中获得。

有时，您需要列出在特定命名空间中运行的所有用户查询。因此，必须在 `"ns"` 字段上过滤之前的输出。下面是一个实现要筛选的输出的示例查询。该查询列出当前在数据库 `"db"` 和集合 `"test"`（即 `"db.test"` 命名空间）中运行的所有用户查询。

```
db.adminCommand({aggregate: 1,
    pipeline: [{$currentOp: {allUsers: true, idleConnections: true}},
               {$match: {ns: {$eq: "db.test"}}}],
    cursor: {}
});
```

作为系统主用户，您可以查看所有用户的查询以及所有内部系统任务。所有其他用户只能查看其各自的查询。

如果查询和内部系统任务的总数超过默认批处理游标大小，则 `mongo` Shell 将自动生成迭代器对象 `'it'` 以查看其余结果。可继续执行 `'it'` 命令，直到查看完所有结果。

## 如何知道查询何时取得进展？
<a name="user_diagnostics-query_progressing"></a>

用户查询可能由于不够理想的查询计划而运行缓慢，或可能由于资源争用而受阻。调试此类查询是一个多步骤过程，可能需要多次执行相同的步骤。

调试的第一步是列出长时间运行或受阻的所有查询。以下查询列出了运行时间超过 10 秒或正在等待资源的所有用户查询。

```
db.adminCommand({aggregate: 1,
                 pipeline: [{$currentOp: {}},
                            {$match: {$or: [{secs_running: {$gt: 10}},
                                            {WaitState: {$exists: true}}]}},
                            {$project: {_id:0,
                                        opid: 1,
                                        secs_running: 1,
                                        WaitState: 1,
                                        blockedOn: 1,
                                        command: 1}}],
                 cursor: {}
                });
```

定期重复上述查询以确定查询列表更改并识别长时间运行或受阻的查询。

如果相关的查询的输出文档具有 `WaitState` 字段，则表示资源争用是查询运行缓慢或受阻的原因。资源争用可能由 I/O、内部系统任务或其他用户查询导致。

此操作的输出将类似于下文（JSON 格式）。

```
{
    "waitedMS" : NumberLong(0),
    "cursor" : {
        "firstBatch" : [
            {
                "opid" : 201,
                "command" : {
                    "aggregate" : ...
                },
                "secs_running" : 208,
                "WaitState" : "IO"
            }
        ],
        "id" : NumberLong(0),
        "ns" : "admin.$cmd"
    },
    "ok" : 1
}
```

如果不同集合中的很多查询并行运行在同一实例上，或者实例大小对于运行查询的数据集而言过小，则 I/O 可能是瓶颈所在。如果查询是只读查询，您可以通过在不同副本中分隔每个集合的查询来缓解上述情况。对于跨不同集合的并发更新，或者当实例大小对数据集而言过小时，您可以通过向上扩展实例来进行缓解。

如果资源争用是由其他用户查询引起的，则输出文档中的 `"blockedOn"` 字段将具有影响此查询的查询的 `"opid"`。利用 `"opid"`，可沿着所有查询的 `"WaitState"` 和 `"blockedOn"` 字段的链查找位于链头的查询。

如果链头的任务是一个内部任务，那么在这种情况下的唯一缓解措施将是，终止查询之后重新运行它。

下面是一个示例输出，其中的查找查询在由另一个任务拥有的集合锁上被阻止。

```
{
    "inprog" : [
        {
            "client" : "...",
            "desc" : "Conn",
            "active" : true,
            "killPending" : false,
            "opid" : 75,
            "ns" : "...",
            "command" : {
                "find" : "...",
                "filter" : {

                }
            },
            "op" : "query",
            "$db" : "test",
            "secs_running" : 9,
            "microsecs_running" : NumberLong(9449440),
            "threadId" : 24773,
            "clientMetaData" : {
                "application" : {
                   "name" : "MongoDB Shell"
                },
                "driver" : {
                    ...
                },
                "os" : {
                    ...
                }
            },
            "WaitState" : "CollectionLock",
            "blockedOn" : "INTERNAL"
        },
        {
            "desc" : "INTERNAL"
        },
        {
            "client" : "...",
            ...
            "command" : {
                "currentOp" : 1
            },
            ...
        }
    ],
    "ok" : 1
}
```

如果 `"WaitState"` 具有值 `"Latch"`、`"SystemLock"`、`"BufferLock"`、`"BackgroundActivity"` 或 `"Other"`，则资源争用的来源为内部系统任务。如果这种情况持续了很长时间，则唯一的缓解措施将是，终止查询并在之后重新运行。

## 如何确定系统突然运行缓慢的原因？
<a name="user_diagnostics-speed_change"></a>

以下是系统速度减慢的一些常见原因：
+ 并发查询之间的资源争用过多 
+ 活动并发查询的数量会随着时间的推移而增加
+ 内部系统任务，例如 `"GARBAGE_COLLECTION"`

要监控系统的长期使用情况，请定期运行以下 `"currentOp"` 查询并将结果输出到外部存储。此查询会计算系统中每个命名空间中的查询和操作数。您可以分析系统使用情况的结果以了解系统上的负载并做出适当的决策。

```
db.adminCommand({aggregate: 1,
                 pipeline: [{$currentOp: {allUsers: true, idleConnections: true}},
                            {$group: {_id: {desc: "$desc", ns: "$ns", WaitState: "$WaitState"}, count: {$sum: 1}}}],
                 cursor: {}
                });
```

此查询将返回在每个命名空间中运行的所有查询和所有内部系统任务的聚合，以及每个命名空间的等待状态（如果有）的数量。

此操作的输出将类似于下文（JSON 格式）。

```
{
    "waitedMS" : NumberLong(0),
    "cursor" : {
        "firstBatch" : [
            {
                "_id" : {
                    "desc" : "Conn",
                    "ns" : "db.test",
                    "WaitState" : "CollectionLock"
                },
               "count" : 2
            },
            {
                "_id" : {
                    "desc" : "Conn",
                    "ns" : "admin.$cmd"
                },
                "count" : 1
            },
            {
                "_id" : {
                    "desc" : "TTLMonitor"
                },
                "count" : 1
            }
        ],
        "id" : NumberLong(0),
        "ns" : "admin.$cmd"
    },
    "ok" : 1
}
```

在上述输出中，命名空间 `"db.test"` 中有两个用户查询在集合锁上被阻止，命名空间 `"admin.$cmd"` 中有一个查询，另外还有一个内部 `"TTLMonitor"` 任务。

如果输出表明有许多查询处于受阻等待状态，请参阅[如何查找和终止长时间运行或受阻的查询？](#user_diagnostics-query_terminating)

## 如何确定一个或多个集群实例上 CPU 使用率过高的原因？
<a name="user_diagnostics-cpu_utilization"></a>

以下各节可以帮助您确定实例 CPU 使用率过高的原因。根据工作负载，您的结果可能会有所不同。
+ 要确定实例突然运行缓慢的原因，请参阅[如何确定系统突然运行缓慢的原因？](#user_diagnostics-speed_change) 
+ 要确定并终止特定实例上长时间运行的查询，请参阅[如何查找和终止长时间运行或受阻的查询？](#user_diagnostics-query_terminating) 
+ 要了解查询是否取得进展，请参阅[如何知道查询何时取得进展？](#user_diagnostics-query_progressing) 
+ 要确定查询长时间运行的原因，请参阅[如何查看查询计划和优化查询？](#user_diagnostics-query_plan)
+ 要跟踪一段时间内长时间运行的查询，请参阅 [分析 Amazon DocumentDB 操作](profiling.md)。

根据实例 CPU 使用率高的原因，执行以下一项或多项操作可能会有所帮助。
+ 如果主实例的 CPU 利用率较高，但副本实例没有，请考虑通过客户端读取首选项设置（例如 `secondaryPreferred`）在副本之间分配读取流量。有关更多信息，请参阅 [作为副本集连接到 Amazon DocumentDB](connect-to-replica-set.md)。

  使用副本进行读取可以让主实例处理更多的写入流量，从而更好地利用集群的资源。从副本中读取的数据最终是一致的。
+ 如果写入工作负载导致高 CPU 使用率，则将集群实例的大小更改为更大的实例类型会增加可用于为该工作负载提供服务的 CPU 内核数量。有关更多信息，请参阅[实例](what-is.md#what-is-db-instances)和[实例类规格](db-instance-classes.md#db-instance-class-specs)。
+ 如果所有集群实例都表现出较高的 CPU 利用率，并且工作负载使用副本进行读取，则向集群添加更多副本会增加可用于读取流量的资源。有关更多信息，请参阅 [向集群添加 Amazon DocumentDB 实例](db-instance-add.md)。

## 如何确定实例上打开的光标？
<a name="user_diagnostics-open_cursors"></a>

连接到 Amazon DocumentDB 实例后，您可以使用命令 `db.runCommand("listCursors")` 列出该实例上打开的光标。在给定的 Amazon DocumentDB 实例上，在任何给定时间最多只能打开 4,560 个活动光标，具体取决于实例类型。通常建议关闭不再使用的游标，因为游标占用实例上的资源并具有上限。有关具体限制，请参阅 [Amazon DocumentDB 配额和限制](limits.md)。

```
db.runCommand("listCursors") 
```

## 如何确定当前的 Amazon DocumentDB 引擎版本？
<a name="user_diagnostics-engine_version"></a>

要确定当前的 Amazon DocumentDB 引擎版本，请运行以下命令。

```
db.runCommand({getEngineVersion: 1})
```

此操作的输出将类似于下文（JSON 格式）。

```
{ "engineVersion" : "2.x.x", "ok" : 1 }
```

**注意**  
亚马逊 DocumentDB 3.6 的引擎版本为 1.x.x，亚马逊 DocumentDB 4.0 的引擎版本为 2.x.x，亚马逊 DocumentDB 5.0 的引擎版本为 3.x.x，亚马逊 DocumentDB 8.0 的引擎版本为 4.x.x。

## 如何分析索引使用情况并识别未使用的索引？
<a name="user-diag-index-usage"></a>

要识别给定集合的索引，请运行以下命令：

```
db.collection.getIndexes()
```

要分析在对集合执行操作期间使用了多少索引，可以使用 `collStats` 和 `indexStats` 命令。要查看使用索引（索引扫描）执行的扫描总数与不使用索引执行的扫描数（集合扫描）的对比，请运行以下命令：

```
db.collection.stats()
```

此命令的输出包括以下值：
+ **`idxScans`**：使用索引对该集合执行的扫描次数。
+ **`collScans`**：不使用索引对此集合执行的扫描次数。这些扫描将涉及逐一查看馆藏中的文档。
+ **`lastReset`**：上次重置这些计数器的时间。当集群或扩展 up/down 实例时 starting/stopping ，此命令提供的统计数据将被重置。

在以下命令的输出中可以找到每个索引的使用量明细。为了提高性能和降低成本，最佳做法是定期识别和删除未使用的索引，因为这样可以省去不必要的计算、存储，并 I/Os 用于维护索引。

```
db.collection.aggregate([{$indexStats:{}}]).pretty()
```

此命令的输出给出了在集合上创建的每个索引的以下值：
+ **`ops`**：使用索引的操作数。如果您的工作负载已经运行了足够长的时间，并且您确信自己的工作负载处于稳定状态，则 `ops` 值为零表示根本没有使用索引。
+ **`numDocsRead`**：使用此索引进行操作期间读取的文档数。
+ **`since`**：自 Amazon DocumentDB 开始收集索引使用统计数据以来的时间，该值通常是自上次数据库重启或维护操作以来的值。
+ **`size`**：索引的大小以字节为单位。

下面的示例是从以上命令中的样本输出。

```
{
    "name" : "_id_",
    "key" : {
        "_id" : 1
    },
    "host" : "example-host.com:12345",
    "size" : NumberLong(...),
    "accesses" : {
        "ops" : NumberLong(...),
        "docsRead" : NumberLong(...),
        "since" : ISODate("...")
    },
    "cacheStats" : {
        "blksRead" : NumberLong(...),
        "blksHit" : NumberLong(...),
        "hitRatio" : ...
    }
}
{
    "name" : "x_1",
    "key" : {
        "x" : 1
    },
    "host" : "example-host.com:12345",
    "size" : NumberLong(...),
    "accesses" : {
        "ops" : NumberLong(...),
        "docsRead" : NumberLong(...),
        "since" : ISODate("...")
    },
    "cacheStats" : {
        "blksRead" : NumberLong(...),
        "blksHit" : NumberLong(...),
        "hitRatio" : ...
    }
}
```

要确定集合的整体索引大小，请运行以下命令：

```
db.collection.stats()
```

要删除未使用的索引，请运行以下命令：

```
db.collection.dropIndex("indexName")
```

## 如何识别缺失的索引？
<a name="user_diagnostics-identify_missing_indexes"></a>

您可以使用 [Amazon DocumentDB 分析器来记录慢速查询](https://docs.aws.amazon.com//documentdb/latest/developerguide/profiling.html)。在慢速查询日志中反复出现的查询可能表示需要额外的索引才能提高该查询的性能。

您可以通过寻找具有一个或多个阶段至少执行一个 `COLLSCAN` 阶段的长时间运行的查询来发现有用索引的机会，这意味着查询阶段必须读取集合中的每个文档才能对查询提供响应。

以下示例显示了对在大型集合上运行的出租车乘车集合的查询。

```
db.rides.count({"fare.totalAmount":{$gt:10.0}}))
```

为了执行此示例，查询必须执行集合扫描（即读取集合中的每个文档），因为 `fare.totalAmount` 字段上没有索引。此查询的 Amazon DocumentDB 分析器输出如下所示：

```
{
    ...
    "cursorExhausted": true,
    "nreturned": 0, 
    "responseLength": 0,
    "protocol": "op_query",
    "millis": 300679,
    "planSummary": "COLLSCAN",
    "execStats": {
        "stage": "COLLSCAN",
        "nReturned": "0",
        "executionTimeMillisEstimate": "300678.042"
    },
    "client": "172.31.5.63:53878",
    "appName": "MongoDB Shell",
    "user": "example"
}
```

为了加快本示例中的查询速度，您需要在 `fare.totalAmount` 上创建索引，如下所示。

```
db.rides.createIndex( {"fare.totalAmount": 1}, {background: true} )
```

**注意**  
在前台创建的索引（即如果在创建索引时未提供 `{background:true}` 选项）采用独占写入锁，这可以防止应用程序在索引构建完成之前将数据写入集合。在生产集群上创建索引时，请注意这种潜在影响。创建索引时，我们建议设置 `{background:true}`。

通常，您希望在具有高基数的字段（例如，大量唯一值）上创建索引。在基数较低的字段上创建索引可能会导致不使用的索引过大。Amazon DocumentDB 查询优化器在创建查询计划时会考虑集合的总体大小和索引的选择性。有时候，即使存在索引，您也会看到查询处理器选择一个 `COLLSCAN`。当查询处理器估计使用索引不会比扫描整个集合更具性能优势时，就会发生这种情况。如果要强制查询处理器使用特定的索引，则可以使用如下所示的 `hint()` 运算符。

```
db.collection.find().hint("indexName")
```

## 如何确定数据库集合膨胀？
<a name="performance-collection-bloat"></a>

当集合的大小因失效或过时的文档累积或数据库页面内的碎片累积而变大时，就会出现集合膨胀。报告的百分比表示未来文档可以使用的文档空间量。此膨胀会同时消耗缓冲区缓存和存储空间中的空间。要消除膨胀，必须在维护时段内通过 dump/restore 或使用迁移环回和切换来重新加载集合。

**Example 示例**  
运行以下命令以确定集合未使用的存储空间：  

```
db.runCommand({collStats:'coll'})
```
结果类似如下：  

```
{
        "ns" : "test.coll",
        "count" : 7500,
        "size" : 23250,
        "avgObjSize" : 31,
        "storageSize" : 106496,
        "unusedStorageSize" : {
                "unusedBytes" : 16384,
                "unusedPercent" : 25.12
        },
        "compression" : {
                "enable" : false
        },
        "capped" : false,
        "nindexes" : 1,
        "totalIndexSize" : 57344,
        "indexSizes" : {
                "_id_" : 57344
        },
        "collScans" : 4,
        "idxScans" : 10000,
        "opCounter" : {
                "numDocsIns" : 1000,
                "numDocsUpd" : 0,
                "numDocsDel" : 250
        },
        "cacheStats" : {
                "collBlksHit" : 3570,
                "collBlksRead" : 8,
                "collHitRatio" : 99.7765,
                "idxBlksHit" : 12293,
                "idxBlksRead" : 6,
                "idxHitRatio" : 99.9513
        },
        "lastReset" : "2024-12-18 00:30:21.552019+00",
        "ok" : 1,
        "operationTime" : Timestamp(1734632375, 1)
}
```

## 有用查询的摘要
<a name="user_diagnostics-useful_queries"></a>

以下查询可用于监控 Amazon DocumentDB 中的性能和资源利用率。
+ 使用以下命令查看有关特定集合的统计信息，包括操作计数器、缓存统计信息、访问统计信息和大小统计信息：

  ```
  db.collection.stats()
  ```
+ 使用以下命令查看有关在集合上创建的每个索引的统计信息，包括索引的大小、索引特定的缓存统计信息和索引使用情况统计信息：

  ```
  db.collection.aggregate([{$indexStats:{}}]).pretty()
  ```
+ 使用以下查询列出所有活动。

  ```
  db.adminCommand({currentOp: 1, $all: 1});
  ```
+ 以下代码列出了所有长时间运行或已阻止的查询。

  ```
  db.adminCommand({aggregate: 1,
                   pipeline: [{$currentOp: {}},
                              {$match: {$or: [{secs_running: {$gt: 10}},
                                              {WaitState: {$exists: true}}]}},
                              {$project: {_id:0,
                                          opid: 1,
                                          secs_running: 1,
                                          WaitState: 1,
                                          blockedOn: 1,
                                          command: 1}}],
                   cursor: {}
                  });
  ```
+ 以下代码终止查询。

  ```
  db.adminCommand({killOp: 1, op: <opid of running or blocked query>});
  ```
+ 使用以下代码获取系统状态的汇总视图。

  ```
  db.adminCommand({aggregate: 1,
                   pipeline: [{$currentOp: {allUsers: true, idleConnections: true}},
                              {$group: {_id: {desc: "$desc", ns: "$ns", WaitState: "$WaitState"}, count: {$sum: 1}}}],
                   cursor: {}
                  });
  ```

# Amazon DocumentDB 中的垃圾回收
<a name="garbage-collection"></a>

Amazon DocumentDB 实现了多版本并发控制（MVCC）数据库架构，可为每次更新操作创建新版本的文档和索引条目。该架构提供了事务隔离，防止一个事务的更改出现在另一个事务中。

**Topics**
+ [了解 Amazon DocumentDB 中的垃圾收集](#understanding-garbage-collection)
+ [垃圾回收过程](#garbage-collection-process)
+ [存储架构和扩展存储](#storage-architecture)
+ [监控垃圾回收](#monitoring-garbage-collection)
+ [collStats 输出示例](#example-collstats-output)
+ [常见问题](#garbage-collection-faq)

## 了解 Amazon DocumentDB 中的垃圾收集
<a name="understanding-garbage-collection"></a>

垃圾回收（GC）是一个自动化后台过程，可在 Amazon DocumentDB 中维持最佳的系统性能和可用性。与许多现代数据库一样，Amazon DocumentDB 的 MVCC 架构每次更新都会创建新的文档和索引版本。每个写入操作都会消耗有限计数器中唯一的 MVCC ID。它们 IDs 标识文档版本属于哪个事务，以及该交易是已提交还是已回退。随着时间的推移，这些旧版本及其 MVCC IDs 会不断累积，需要进行清理以防止性能下降。

### 垃圾回收的功能
<a name="w2aac49c15b7b5"></a>

垃圾回收器有三个基本功能：
+ **回收存储空间** – 将移除活动查询不再需要的过时文档和索引版本，从而为将来的写入操作释放空间。
+ **防止 MVCC ID 溢出** — 它通过管理 MVCC 的有限计数器来防止 MVCC ID 溢出。 IDs如果没有这种管理，计数器最终将达到其极限，从而迫使数据库进入临时的只读模式，直到 IDs 被回收为止。
+ **维持查询性能** – 通过消除失效的文档版本来维持最佳的查询性能，否则这些文档版本会累积并拖慢查询处理速度。

## 垃圾回收过程
<a name="garbage-collection-process"></a>

垃圾回收过程对每个集合进行操作，并且可以在不同的集合上并发运行多个过程。此过程包括四个连续阶段：

1. **识别** – 系统识别活动事务或查询不再引用的文档和索引版本。

1. **内存加载** – 如果旧文档和索引条目尚未存在于内存中，则会将其加载到内存中。

1. **删除** – 永久删除过时版本以回收存储空间。

1. **MVCC ID 回收** — 系统 IDs 从已删除的版本中回收 MVCC 以进行新操作。

当垃圾收集完成对旧文档版本的处理后，它会 IDs 从系统中删除最旧的 MVCC。这种清理对于通过回收 MVCC 来防止 MVCC ID 溢出至关重要 IDs，使它们可用于整个集群中的新写入操作。若无此回收过程，系统最终将耗尽其有限的 MVCC ID 计数器并进入只读状态。

### 垃圾回收调度
<a name="w2aac49c15b9b9"></a>

垃圾回收会定期在后台自动运行。时间和频率会根据系统负载、可用资源、写入量和 MVCC ID 消耗水平进行动态调整。在高写入活动期间，垃圾回收过程将更频繁地执行以管理数量激增的文档版本。

## 存储架构和扩展存储
<a name="storage-architecture"></a>

Amazon DocumentDB 使用复杂的存储架构，将文档存储分为两个不同的部分：

### 基础存储段
<a name="w2aac49c15c11b5"></a>

基本存储段包含主要文档数据和元数据。此区段存储：
+ 符合标准页面大小 (8 KB) 的文档内容。
+ 文档元数据和结构信息。
+ 主索引及其条目。
+ 集合级别的统计数据和配置。

### 扩展存储段
<a name="w2aac49c15c11b7"></a>

扩展存储段使用专门的大型文档对象存储，用于处理超过标准存储页面大小的文档。本部分提供：
+ **高效的大型文档处理** — 大于基本存储阈值的文档会自动移至扩展存储段。
+ **优化的存储布局** — 该分段使用针对大型对象进行了优化的不同存储格式，从而减少了碎片并改善了访问模式。
+ **独立垃圾收集** — 扩展存储段有自己的垃圾收集过程，该过程可以独立于基本存储清理运行。
+ **透明访问** — 应用程序可以无缝访问大型文档，而无需知道哪个存储段包含数据。

扩展存储区段对以下方面特别有利：
+ 包含大型嵌入式数组的文档的集合。
+ 具有大量嵌套结构的文档。
+ 存储二进制数据或大型文本字段的集合。
+ 具有混合文档大小的应用程序，其中一些文档大大超过平均大小。

## 监控垃圾回收
<a name="monitoring-garbage-collection"></a>

### 集群级别指标
<a name="w2aac49c15c13b3"></a>

**`AvailableMVCCIds`**
+ **地点**-亚马逊 CloudWatch
+ **描述**-一个计数器，显示从上限 18 亿个起可用的剩余写入操作数。当此计数器达到零时，您的集群将进入只读模式，直到 IDs 被回收和回收。计数器会随着每次写入操作而减少，并随着垃圾收集回收旧的 M IDs VCC 而增加。
+ **建议** – 当值低于 13 亿时设置警报。此预警可让您采取稍后讨论的建议步骤。

**`LongestActiveGCRuntime`**
+ **地点**-亚马逊 CloudWatch
+ **描述** – 最长活动垃圾回收过程的持续时间（以秒为单位）。每分钟更新一次，仅跟踪活动操作，不包括在一分钟时段内完成的进程。
+ **建议**-与`gcRuntimeStats`历史数据进行比较，以识别异常的垃圾收集行为，例如批量删除期间的运行时间延长。

### 回收级别指标
<a name="w2aac49c15c13b5"></a>

**`MVCCIDStats: MVCCIdScale`**
+ **位置** – 数据库 collStats 命令
+ **描述** – 以 0 到 1 的等级衡量 MVCC ID 使用期限，其中 1 表示集群进入只读状态前的最长使用期限。同时使用此指标`AvailableMVCCIds`来识别包含使集群老化的最旧 MVCC IDs 的集合。
+ **建议** – 将每个集合的值维持在 0.3 以下。

**`gcRuntimeStats`**
+ **位置** – 数据库 collStats 命令
+ **描述** – 提供两个月的垃圾回收指标历史记录，包括总运行次数、平均持续时间和最长持续时间。仅包括持续时间超过五分钟的垃圾回收操作，以确保有意义的统计数据。

**重要**  
`gcRuntimeStats``documentFragmentStats`、，以及将馆藏级别的指标分解为`storageSegmentBase`并`storageSegmentExtended`仅适用于亚马逊 DocumentDB 8.0。

**`storageSizeStats`**
+ **位置** – 数据库 collStats 命令
+ **描述** — 提供不同存储段的存储利用率的详细明细：
  + `storageSegmentBase`— 基础存储段用于存储标准文档的存储空间
  + `storageSegmentExtended`— 扩展存储段用于存储大型文档的存储空间
+ **用法**-帮助识别具有大量文件存储空间的馆藏，并了解存储分配模式。

**`unusedStorageSize`**（集合级别）
+ **位置** – 数据库 collStats 命令
+ **描述** – 根据抽样统计数据估算集合中未使用的存储空间。包括已删除文档和空分段所占用的空间。该指标同时提供合并总数和每个区段的细分：
  + 合并`unusedBytes`并`unusedPercent`跨所有存储段
  + `storageSegmentBase`— 未使用的空间，特别是基础存储段中的未使用空间
  + `storageSegmentExtended`— 未使用的空间，特别是在扩展存储段中

**`documentFragmentStats`**
+ **位置** – 数据库 collStats 命令
+ **描述**-提供有关文档片段和集合中无效数据的详细信息。文档片段代表数据库引擎使用的内部存储单元，而失效的碎片表示无法再访问但尚未被回收的数据。该指标包括：
  + `totalDocFragmentsCount`— 馆藏中的文档片段总数
  + `deadDocFragmentsCount`— 包含失效（无法访问）数据的片段数
  + `deadDocFragmentsPercent`— 包含失效数据的片段的百分比
  + `deadDocFragmentBytes`— 失效文档片段消耗的估计字节数
  + 和的按区段`storageSegmentBase`细分 `storageSegmentExtended`
+ **使用情况**-监控此指标以了解垃圾收集的有效性，并确定可能从维护操作中受益的收集。高百分比的死碎片表明垃圾收集可能落后，或者垃圾收集将从优化中受益。

### 索引级别指标
<a name="w2aac49c15c13b7"></a>

**`unusedStorageSize`**（索引级别）
+ **位置** – 数据库 indexStats 命令
+ **描述** – 根据抽样统计数据估算索引中未使用的存储空间。包括过时的索引条目和空分段所占用的空间。
+ **建议** – 使用 `reIndex` 命令在不停机的情况下重建索引并回收未使用的空间。有关更多详细信息，请参阅“管理索引”。

## collStats 输出示例
<a name="example-collstats-output"></a>

以下示例显示了包含垃圾收集和存储指标的典型`collStats`输出：

```
{
    "ns" : "Mvcc_consumption_test_db.mvcc_test_collection",
    "MVCCIdStats" : {
        "MVCCIdScale" : 0.03
    },
    "gcRuntimeStats" : {
        "numRuns" : 1,
        "historicalAvgRuntime" : 3295,
        "historicalMaxRuntime" : 3295,
        "lastRuntime" : 3295,
        "lastRuntimeStart" : ISODate("2025-06-24T08:47:14Z")
    },
    "documentFragmentStats" : {
        "totalDocFragmentsCount" : 45000000,
        "deadDocFragmentsCount" : 2250000,
        "deadDocFragmentsPercent" : 5.0,
        "deadDocFragmentBytes" : 98304000,
        "storageSegmentBase" : {
            "totalDocFragmentsCount" : 30000000,
            "deadDocFragmentsCount" : 1500000,
            "deadDocFragmentsPercent" : 5.0,
            "deadDocFragmentBytes" : 65536000
        },
        "storageSegmentExtended" : {
            "totalDocFragmentsCount" : 15000000,
            "deadDocFragmentsCount" : 750000,
            "deadDocFragmentsPercent" : 5.0,
            "deadDocFragmentBytes" : 32768000
        }
    },
    "collScans" : 14,
    "count" : 30000000,
    "size" : 1320000000,
    "avgObjSize" : 44,
    "storageSize" : 6461497344,
    "storageSizeStats" : {
        "storageSegmentBase" : 4307664896,
        "storageSegmentExtended" : 2153832448
    },
    "capped" : false,
    "nindexes" : 2,
    "totalIndexSize" : 9649553408,
    "indexSizes" : {
        "_id_" : 1910661120,
        "c_1" : 7738892288
    },
    "unusedStorageSize" : {
        "unusedBytes" : 4201881600,
        "unusedPercent" : 65.05,
        "storageSegmentBase" : {
            "unusedBytes" : 2801254400,
            "unusedPercent" : 65.05
        },
        "storageSegmentExtended" : {
            "unusedBytes" : 1400627200,
            "unusedPercent" : 65.05
        }
    },
    "cacheStats" : {
        "collBlksHit" : 171659016,
        "collBlksRead" : 754061,
        "collHitRatio" : 99.5627,
        "idxBlksHit" : 692563636,
        "idxBlksRead" : 1177921,
        "idxHitRatio" : 99.8303
    },
    "idxScans" : 41823984,
    "opCounter" : {
        "numDocsIns" : 0,
        "numDocsUpd" : 20911992,
        "numDocsDel" : 0
    },
    "lastReset" : "2025-06-24 05:57:08.219711+00",
    "ok" : 1,
    "operationTime" : Timestamp(1750968826, 1)
}
```

## 常见问题
<a name="garbage-collection-faq"></a>

### 如何识别垃圾回收效率低下？
<a name="w2aac49c15c17b3"></a>

监控以下表明垃圾回收效率低下的警告信号：
+ **集合膨胀过多 — 在**大量写入或批量删除期间，`unusedStorageSize`指标会稳步增加，尤其是在索引较大的情况下。
+ **高死亡碎片百分比** — `documentFragmentStats` 显示的`deadDocFragmentsPercent`值一直很高（高于 10-15%）。
+ **查询延迟降低**-由于累积的失效文档导致查询延迟增加。
+ **GC 持续时间延长** — 垃圾收集操作花费的时间比历史平均值长`gcRuntimeStats`。
+ **GC 处理能力提升** — 高`LongestActiveGCRuntime`表示垃圾收集器无法满足系统需求。

### 垃圾回收会影响我的数据库性能吗？
<a name="w2aac49c15c17b5"></a>

正常情况下，垃圾回收对性能的影响微乎其微。但是，当垃圾回收滞后时，您可能会遇到：
+ 积累的失效文档导致存储成本增加。
+ 由于索引条目过时，查询性能降低。
+ 如果 MVCC 已耗尽，则 IDs 为临时只读模式。
+ 在密集收集运行期间，资源使用量更高，尤其是在较小的实例上。
+ 降低了大型文档的扩展存储段操作效率。

### 我是否可以手动触发垃圾回收？
<a name="w2aac49c15c17b7"></a>

不可以，无法手动触发 Amazon DocumentDB 中的垃圾回收。作为内部维护操作的一部分，系统会自动管理垃圾回收。

### 作为操作最佳实践，我应该设置哪些警报？
<a name="w2aac49c15c17b9"></a>

我们建议在集群级别和集合级别设置监控，以确保您的 Amazon DocumentDB 系统实现最佳性能。

要进行集群级别的监控，首先要为阈值为 13 亿的`AvailableMVCCIds`指标创建 Amazon CloudWatch 警报。这使您有足够的时间在指标达到零（届时您的集群将进入只读模式）之前采取措施。请记住，该指标可能会根据您的具体使用模式而波动——一些客户看到该指标降至13亿以下，然后在垃圾收集完成工作后恢复到15亿以上。

通过Amazon监控`LongestActiveGCRuntime`指标也很重要 CloudWatch。此指标与 `gcRuntimeStats` 一起可帮助您了解整个系统中垃圾回收的执行效率。

对于集合级别的监控，请重点关注以下关键指标：
+ `MVCCIdScale`— 注意不断增加的数值，这些值表明 MVCC IDs 正在老化，可能需要注意。
+ `gcRuntimeStats`— 确定垃圾收集过程耗时异常长或持续多天。
+ `documentFragmentStats`— 监控`deadDocFragmentsPercent`值——持续较高的百分比（高于 10-15%）可能表明垃圾收集落后。
+ `storageSizeStats`以及 `unusedStorageSize` — 跟踪存储利用模式，识别两个存储段中存在大量未使用空间的馆藏。

需要特别注意具有频繁写入操作的集合，因为其会增加垃圾收集器的工作。对于具有大量写入活动的集合，我们建议更频繁地检查这些指标，以确保垃圾回收能跟上您的工作负载。

请注意，这些监控建议仅作为起点。随着您对系统行为越来越熟悉，您可能需要调整这些阈值以更好地匹配您的具体使用模式和要求。

### 如果我的 `AvailableMVCCIds` 低于 13 亿该怎么办？
<a name="w2aac49c15c17c11"></a>

如果您的 `AvailableMVCCIds`指标降至 13 亿以下，我们建议您立即采取措施以防止集群进入只读模式。我们建议先扩展实例大小，为垃圾回收器提供更多计算资源。这是我们的主要建议，因为它允许您的应用程序继续正常运行，同时为垃圾收集器提供 catch 所需的额外功能。

如果仅靠扩展并不能改善状况，我们建议您考虑减少写入操作。使用该`MVCCIdScale`指标来确定哪些特定集合包含需要注意的较旧 MVCC IDs 。此外，还`documentFragmentStats`要进行监控以识别可能导致垃圾收集效率低下的死碎片百分比高的集合。

识别出这些集合后，可能需要暂时减少针对其的写入操作，以便垃圾回收能够跟上节奏。在恢复期间，我们建议您密切监控 `AvailableMVCCIds` 指标，以确保措施达到预期效果。`AvailableMVCCIds` 值恢复到 15 亿或更高后，您的集群就会被视为运行状况良好。

请记住，这些步骤属于预防性措施，旨在帮助系统在达到紧急状态前恢复。在看到指标降至 13 亿以下后，您越早采取措施，就越有可能避免对写入操作造成任何影响。