性能和资源利用率

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

如何查找并终止长时间运行或受阻的查询?

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

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

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

  1. db.adminCommand({
  2. aggregate: 1,
  3. pipeline: [
  4. {$currentOp: {}},
  5. {$match:
  6. {$or: [
  7. {secs_running: {$gt: 10}},
  8. {WaitState: {$exists: true}}]}},
  9. {$project: {_id:0, opid: 1, secs_running: 1}}],
  10. cursor: {}
  11. });

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

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

  1. 查找查询的 opid

    1. db.adminCommand({
    2. aggregate: 1,
    3. pipeline: [
    4. {$currentOp: {}},
    5. {$match:
    6. {$or:
    7. [{secs_running: {$gt: 10}},
    8. {WaitState: {$exists: true}}]}}],
    9. cursor: {}
    10. });

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

    1. {
    2. "waitedMS" : NumberLong(0),
    3. "cursor" : {
    4. "firstBatch" : [
    5. {
    6. "opid" : 24646,
    7. "secs_running" : 12
    8. }
    9. ],
    10. "id" : NumberLong(0),
    11. "ns" : "admin.$cmd"
    12. },
    13. "ok" : 1
    14. }
  2. 使用 killOp 操作终止查询。

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

如何查看查询计划和优化查询?

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

注意

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

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

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

以下是操作示例。

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

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

  1. {
  2. "queryPlanner" : {
  3. "plannerVersion" : 1,
  4. "namespace" : "db.test",
  5. "winningPlan" : {
  6. "stage" : "COLLSCAN"
  7. }
  8. },
  9. "serverInfo" : {
  10. "host" : "...",
  11. "port" : ...,
  12. "version" : "..."
  13. },
  14. "ok" : 1
  15. }

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

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

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

  1. {
  2. "queryPlanner" : {
  3. "plannerVersion" : 1,
  4. "namespace" : "db.test",
  5. "winningPlan" : {
  6. "stage" : "IXSCAN",
  7. "indexName" : "x_1",
  8. "direction" : "forward"
  9. }
  10. },
  11. "serverInfo" : {
  12. "host" : "...",
  13. "port" : ...,
  14. "version" : "..."
  15. },
  16. "ok" : 1
  17. }

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

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

如何列出实例上正在运行的所有操作?

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

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

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

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

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

  1. {
  2. "inprog" : [
  3. {
  4. "desc" : "INTERNAL"
  5. },
  6. {
  7. "desc" : "TTLMonitor",
  8. "active" : false
  9. },
  10. {
  11. "desc" : "GARBAGE_COLLECTION"
  12. },
  13. {
  14. "client" : ...,
  15. "desc" : "Conn",
  16. "active" : true,
  17. "killPending" : false,
  18. "opid" : 195,
  19. "ns" : "admin.$cmd",
  20. "command" : {
  21. "currentOp" : 1,
  22. "$all" : 1
  23. },
  24. "op" : "command",
  25. "$db" : "admin",
  26. "secs_running" : 0,
  27. "microsecs_running" : NumberLong(68),
  28. "clientMetaData" : {
  29. "application" : {
  30. "name" : "MongoDB Shell"
  31. },
  32. "driver" : {
  33. ...
  34. },
  35. "os" : {
  36. ...
  37. }
  38. }
  39. }],
  40. "ok" : 1
  41. }

以下是 "desc" 字段的有效值:

  • INTERNAL — 内部系统任务,如游标清理或过时用户清理任务。

  • TTLMonitor — 生存时间 (TTL) 监控线程。其运行状态在 "active" 字段中反映。

  • GARBAGE_COLLECTION — 内部垃圾收集器线程。系统中最多可以同时运行三个垃圾收集器线程。

  • CONN — 用户查询。

  • CURSOR — 操作是空闲游标,它等待用户调用“getMore”命令以获取下一批结果。在此状态下,游标会占用内存,但不会消耗任何计算。

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

有时,您需要列出在特定命名空间中运行的所有用户查询。因此,先前的输出必须在 "ns" 字段上进行筛选。以下是一个示例查询,用于实现要筛选的输出。此查询列出了当前在数据库 "db" 和集合 "test"(即 "db.test" 命名空间)中运行的所有用户查询。

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

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

如果查询和内部系统任务的总数超过默认批处理光标大小,mongo shell 会自动生成迭代器对象 'it' 以查看其余结果。继续执行 'it' 命令,直到所有结果用尽。

我如何知道何时进行查询?

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

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

  1. db.adminCommand({aggregate: 1,
  2. pipeline: [{$currentOp: {}},
  3. {$match: {$or: [{secs_running: {$gt: 10}},
  4. {WaitState: {$exists: true}}]}},
  5. {$project: {_id:0,
  6. opid: 1,
  7. secs_running: 1,
  8. WaitState: 1,
  9. blockedOn: 1,
  10. command: 1}}],
  11. cursor: {}
  12. });

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

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

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

  1. {
  2. "waitedMS" : NumberLong(0),
  3. "cursor" : {
  4. "firstBatch" : [
  5. {
  6. "opid" : 201,
  7. "command" : {
  8. "aggregate" : ...
  9. },
  10. "secs_running" : 208,
  11. "WaitState" : "IO"
  12. }
  13. ],
  14. "id" : NumberLong(0),
  15. "ns" : "admin.$cmd"
  16. },
  17. "ok" : 1
  18. }

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

如果资源争用由其他用户查询导致,则输出文档中的 "blockedOn" 字段将具有影响此查询的查询的 "opid"。使用 "opid" 可沿着所有查询的 "WaitState""blockedOn" 字段链查找位于链头的查询。

如果链头的任务是一个内部任务,则在这种情况下的唯一缓解措施将是终止查询并在稍后重新运行。

以下是示例输出,其中的查找查询在由其他任务拥有的集合锁上被阻止。

  1. {
  2. "inprog" : [
  3. {
  4. "client" : "...",
  5. "desc" : "Conn",
  6. "active" : true,
  7. "killPending" : false,
  8. "opid" : 75,
  9. "ns" : "...",
  10. "command" : {
  11. "find" : "...",
  12. "filter" : {
  13. }
  14. },
  15. "op" : "query",
  16. "$db" : "test",
  17. "secs_running" : 9,
  18. "microsecs_running" : NumberLong(9449440),
  19. "threadId" : 24773,
  20. "clientMetaData" : {
  21. "application" : {
  22. "name" : "MongoDB Shell"
  23. },
  24. "driver" : {
  25. ...
  26. },
  27. "os" : {
  28. ...
  29. }
  30. },
  31. "WaitState" : "CollectionLock",
  32. "blockedOn" : "INTERNAL"
  33. },
  34. {
  35. "desc" : "INTERNAL"
  36. },
  37. {
  38. "client" : "...",
  39. ...
  40. "command" : {
  41. "currentOp" : 1
  42. },
  43. ...
  44. }
  45. ],
  46. "ok" : 1
  47. }

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

如何确定系统突然运行缓慢的原因?

以下是系统减速的一些常见原因:

  • 并发查询之间的资源争用过多

  • 活动并发查询的数量随时间增加

  • 内部系统任务,如 "GARBAGE_COLLECTION"

要监控系统的长期使用情况,请定期运行以下 "currentOp" 查询并将结果输出到外部存储。此查询会计算系统中每个命名空间中的查询和操作数。然后,您可以分析系统使用情况结果以了解系统上的负载并采取相应措施。

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

此查询返回在每个命名空间中运行的所有查询、所有内部系统任务以及每个命名空间的等待状态(如果有)的唯一数量的聚合。

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

  1. {
  2. "waitedMS" : NumberLong(0),
  3. "cursor" : {
  4. "firstBatch" : [
  5. {
  6. "_id" : {
  7. "desc" : "Conn",
  8. "ns" : "db.test",
  9. "WaitState" : "CollectionLock"
  10. },
  11. "count" : 2
  12. },
  13. {
  14. "_id" : {
  15. "desc" : "Conn",
  16. "ns" : "admin.$cmd"
  17. },
  18. "count" : 1
  19. },
  20. {
  21. "_id" : {
  22. "desc" : "TTLMonitor"
  23. },
  24. "count" : 1
  25. }
  26. ],
  27. "id" : NumberLong(0),
  28. "ns" : "admin.$cmd"
  29. },
  30. "ok" : 1
  31. }

在前面的输出中,命名空间 "db.test" 中的两个用户查询在集合锁定时被阻止:命名空间 "admin.$cmd" 中的一个查询和一个内部 "TTLMonitor" 任务。

如果输出表明有许多查询处于受阻等待状态,请参阅如何查找并终止长时间运行或受阻的查询?

如何确定一个或多个集群实例上 CPU 使用率高的原因?

以下部分可帮助您确定实例 CPU 使用率高的原因。您的结果可能因工作负载而异。

根据高实例 CPU 使用率的原因,执行以下一项或多项操作可能会有所帮助。

  • 如果主实例显示较高的 CPU 利用率,但副本实例不显示,请考虑通过客户端读取首选项设置(例如 secondaryPreferred)跨副本分配读取流量。有关更多信息,请参阅连接至 Amazon DocumentDB 作为副本集

    通过允许主实例处理更多写入流量,将副本用于读取可以更好地利用集群的资源。从副本进行读取具有最终一致性。

  • 如果 CPU 利用率高是由写入工作负载导致的,则将集群的实例大小更改为更大的实例类型会增加可用于服务工作负载的 CPU 核心数。有关更多信息,请参阅 Instances实例类规格

  • 如果所有集群实例都表现出较高的 CPU 利用率,并且工作负载使用副本进行读取,则向集群添加更多副本将增加可用于读取流量的资源。有关更多信息,请参阅将 Amazon DocumentDB 实例添加到集群

如何确定实例上的打开光标?

当连接到 Amazon DocumentDB 实例时,您可以使用命令 db.runCommand("listCursors") 列出该实例上已打开的游标。请注意,给定 Amazon DocumentDB 实例上任何给定时间最多有 450 个活动光标处于打开状态。通常建议关闭不再使用的游标,因为游标占用实例上的资源并具有上限。

  1. db.runCommand("listCursors")

如何确定当前的 Amazon DocumentDB 引擎版本?

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

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

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

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

注意

3.6 的引擎版本为 1.x.x,Amazon DocumentDB 4.0 的引擎版本为 2.x.x。Amazon DocumentDB

如何标识未使用的索引?

最佳实践是定期确定和删除未使用的索引以提高性能和降低成本,因为它消除了用于维护索引的不必要的计算、存储和 IOs。要标识给定集合的索引,请运行以下命令:

  1. db.collection.getIndexes()

要确定是否使用了索引,请运行以下命令。命令的输出描述以下内容:

  1. db.collection.aggregate([{$indexStats:{}}]).pretty()
  • **ops** — 使用索引的操作数。如果您的工作负载已运行足够长的时间,并且您确信工作负载处于稳定状态,则 ops 值为零表示根本未使用索引。

  • **since** — 自 Amazon DocumentDB 开始收集索引使用情况统计数据以来的时间,这通常是自上次数据库重新启动或维护操作以来的值。

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

  1. db.collection.stats()

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

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

如何标识缺少的索引?

您可以使用 Amazon DocumentDB 分析器记录慢速查询。在慢速查询日志中重复出现的查询可能指示需要一个额外的索引来提高该查询的性能。

您可以通过查找具有至少一个 COLLSCAN 阶段的一个或多个阶段的长时间运行的查询来识别有帮助的索引的机会,这意味着它们查询阶段必须读取集合中的每个文档以提供对查询的响应。

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

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

为了执行此示例,查询必须执行集合扫描(即读取集合中的每个文档),因为 fare.totalAmount 字段上没有索引。此查询的 Amazon DocumentDB 分析器输出类似于以下内容:

  1. {
  2. ...
  3. "cursorExhausted": true,
  4. "nreturned": 0,
  5. "responseLength": 0,
  6. "protocol": "op_query",
  7. "millis": 300679,
  8. "planSummary": "COLLSCAN",
  9. "execStats": {
  10. "stage": "COLLSCAN",
  11. "nReturned": "0",
  12. "executionTimeMillisEstimate": "300678.042"
  13. },
  14. "client": "172.31.5.63:53878",
  15. "appName": "MongoDB Shell",
  16. "user": "example"
  17. }

要加快此示例中的查询速度,您需要在 fare.totalAmount 上创建一个索引,如下所示。

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

注意

在前台中创建的索引(这意味着,如果创建索引时未提供 {background:true} 选项)将采用互斥的写入锁定,这会阻止应用程序将数据写入集合,直到索引构建完成。在生产集群上创建索引时,请注意这种可能影响。在创建索引时,我们建议设置 {background:true}

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

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

有用查询的摘要

以下查询可用于监控 Amazon DocumentDB 中的性能和资源使用率。

  • 使用以下查询列出所有活动。

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

    1. db.adminCommand({aggregate: 1,
    2. pipeline: [{$currentOp: {}},
    3. {$match: {$or: [{secs_running: {$gt: 10}},
    4. {WaitState: {$exists: true}}]}},
    5. {$project: {_id:0,
    6. opid: 1,
    7. secs_running: 1,
    8. WaitState: 1,
    9. blockedOn: 1,
    10. command: 1}}],
    11. cursor: {}
    12. });
  • 以下代码终止查询。

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

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