如何排查 Amazon RDS 或 Amazon Aurora PostgreSQL 的高 CPU 利用率?
上次更新日期:2022 年 11 月 18 日
我想要确定 Amazon Relational Database Service (Amazon RDS) 或 Amazon Aurora PostgreSQL 兼容版中高 CPU 利用率的原因并进行解决?
简短描述
如果您发现负载的 CPU 利用率很高,则可以使用以下工具的组合来确定原因:
- Amazon CloudWatch 指标
- 增强监控指标
- 性能详情指标
- 本机 PostgreSQL 视图和目录(例如 pg_stat_statements、pg_stat_activity 和 pg_stat_user_tables)
- 数据库中的空闲连接
- Analyze 命令
- PostgreSQL 日志记录参数,用于记录长时间运行的查询、autovacuum、锁定等待以及连接和断开连接请求。有关更多信息,请参阅如何使用 Amazon RDS for PostgreSQL 启用查询日志记录?
解决方法
Amazon CloudWatch 指标
您可以使用 CloudWatch 指标来确定长期的 CPU 模式。将图表 WriteIOPs、ReadIOPs、ReadThroughput 和 WriteThroughput 与 CPU 利用率进行比较,以找到工作负载导致高 CPU 利用率的时间。
确定时间范围后,您可以查看与数据库实例关联的增强监控数据。您可以将增强监控设置为每隔 1、5、10、15、30 或 60 秒收集一次数据。这样做让您可以在比 CloudWatch 更精细的级别收集数据。
增强监控
增强监控提供操作系统(OS)级别的视图。此视图有助于在精细级别上确定高 CPU 负载的原因。例如,您可以查看负载平均值、CPU 分配(system% 或 nice%)和操作系统进程列表。
使用增强监控,以 1 分钟、5 分钟和 15 分钟的间隔检查 loadAverageMinute 数据。负载平均值大于 vCPU 数量,表示该实例负载很重。此外,如果负载平均值小于数据库实例类的 vCPU 数量,则应用程序延迟可能并非由 CPU 节流引起。检查负载平均值,以避免在诊断 CPU 使用原因时出现误报。
例如,假设您的数据库实例使用的是 db.m5.2xlarge 实例类,预调配 IOPS 为 3000,达到了 CPU 限制。然后,您可以查看以下指标示例,以确定 CPU 使用率高的根本原因。在以下示例中,实例类具有与之关联的八个 vCPU。对于相同的负载平均值,超过 170 表示在测量的时间范围内计算机处于重负载:
负载平均分钟
15 | 170.25 |
5 | 391.31 |
1 | 596.74 |
CPU 利用率
用户 (%) | 0.71 |
系统 (%) | 4.9 |
Nice 值 (%) | 93.92 |
总计 (%) | 99.97 |
注意:在增强监控中,Nice% 表示工作负载对数据库使用的 CPU 量。
开启增强监控后,还可检查与数据库实例关联的操作系统进程列表。增强监控显示最多 100 个进程。这可帮助您根据 CPU 和内存使用情况确定哪些进程对性能的影响最大。
您可以将增强监控结果与 pg_stat_activity 结果相结合,以帮助确定查询的资源使用情况。
性能详情
您可以使用 Amazon RDS 性能详情来识别造成数据库负载的查询。您可以选中与特定时间范围对应的 SQL 选项卡来实现。
本机 PostgreSQL 视图和目录
在数据库引擎级别,如果问题实时发生,您可以使用 pg_stat_activity 或 pg_stat_statements。这可帮助您对发送最多流量的计算机、客户端和 IP 地址进行分组。您也可以使用此数据来检查一段时间内的增长情况或应用程序服务器的增加情况。您还可以验证应用程序服务器是否存在会话阻塞或锁定问题。有关更多信息,请参阅有关 pg_stat_activity 和 pg_stat_statements 的 PostgreSQL 文档。
要启用 pg_stat_statements,请修改现有的自定义参数组并设置以下值:
- 将 pg_stat_statements 添加至 shared_preload_libraries
- track_activity_query_size = 4096
- pg_stat_statements.track = ALL
- pg_stat_statements.max = 10000
选择立即应用,然后重启数据库实例。然后,在要监控的数据库上运行与以下内容类似的命令:
注意:以下示例在 "demo" 数据库中安装扩展。
demo=> select current_database();
current_database
------------------
demo
(1 row)
demo=> CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
设置 pg_stat_statements 后,您可以使用以下方法之一监控输出:
按 total_time 列出查询,并查看 PostgreSQL 版本 12 及更低版本中哪个查询在数据库中花费的时间最多:
SELECT total_time, query
FROM pg_stat_statements
ORDER BY total_time DESC LIMIT 10;
对于 PostgreSQL 版本 13 及更高版本:
SELECT total_plan_time+total_exec_time as total_time, query
FROM pg_stat_statements
ORDER BY 1 DESC LIMIT 10;
列出缓冲区缓存命中率较低的查询,对于 PostgreSQL 版本 12 及更低版本,运行以下查询:
SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_time
DESC LIMIT 10;
对于 PostgreSQL 版本 13 及更高版本:
SELECT query, calls, total_plan_time+total_exec_time as total_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit +
shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY 3 DESC LIMIT 10;
对于 PostgreSQL 版本 12 及更低版本,按每次执行列出查询,以便对一段时间内的查询进行采样:
SELECT query, calls, total_time/calls as avg_time_ms, rows/calls as avg_rows,
temp_blks_read/calls as avg_tmp_read, temp_blks_written/calls as avg_temp_written
FROM pg_stat_statements
WHERE calls != 0
ORDER BY total_time DESC LIMIT 10;
对于 PostgreSQL 版本 13 及更高版本:
SELECT query, calls, (total_plan_time+total_exec_time as total_time)/calls as avg_time_ms, rows/calls as avg_rows,
temp_blks_read/calls as avg_tmp_read, temp_blks_written/calls as avg_temp_written
FROM pg_stat_statements
WHERE calls != 0
ORDER BY 3 DESC LIMIT 10;
数据库中的空闲连接
数据库中的空闲连接可能会消耗计算资源,例如内存和 CPU。当实例的 CPU 利用率较高时,请务必检查数据库上的空闲连接。有关更多信息,请参阅 Performance impact of idle PostgreSQL connections(空闲 PostgreSQL 连接对性能的影响)。您可以通过使用增强监视查看操作系统进程列表来检查空闲连接。但是,此列表最多显示 100 个进程。
您可以通过在数据库级别运行几个查询来检查空闲连接:
运行以下查询以查看当前处于空闲和活动状态的会话:
SELECT pid, datname, state, current_timestamp-least(query_start,xact_start) age, application_name, usename, query
FROM pg_stat_activity
WHERE query != '<IDLE>'
AND query NOT ILIKE '%pg_stat_activity%'
AND usename!='rdsadmin'
ORDER BY query_start desc;
SELECT application_name,pid,wait_event_type,wait_event,current_timestamp-least(query_start,xact_start) AS runtime, query AS current_query
FROM pg_stat_activity
WHERE not pid=pg_backend_pid()
AND query NOT ILIKE '%pg_stat_activity%'
AND usename!='rdsadmin';
运行以下查询以获取每个用户和应用程序名称的连接计数:
postgres=> SELECT application_name,count(*) FROM pg_stat_activity GROUP BY application_name;
application_name | count
------------------------+-------
psql | 1
PostgreSQL JDBC Driver | 1
| 5
(3 rows)
postgres=> SELECT usename,count(*) FROM pg_stat_activity GROUP BY usename;
usename | count
----------+-------
master | 4
user1 | 1
rdsadmin | 2
(3 rows)
确定空闲连接后,通过运行以下任一查询来结束这些连接:
psql=> SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename = 'example-username'
AND pid <> pg_backend_pid()
AND state in ('idle');
-或者-
SELECT pg_terminate_backend (example-pid);
如果您的应用程序按设计导致连接过多,则可以考虑对其进行更改,以免内存和 CPU 资源用在管理这些连接上。您可以更改应用程序以限制连接数,也可以使用连接池如 PgBouncer。您还可以使用 Amazon RDS 代理,这是一项托管式服务,只需单击几下即可设置连接池。
Analyze 命令
Analyze 命令收集有关数据库中表内容的统计信息,并将结果存储在 pg_statistic 系统目录中。随后,查询规划器使用这些统计信息来帮助确定最有效的查询执行计划。如果您不经常对数据库中的表运行 Analyze,则查询可能会消耗更多的计算资源。这是因为您访问的关系在系统中存在陈旧的统计信息。在以下情况下可能出现这些问题:
- Autovacuum 不经常运行。
- 主要版本升级后未运行 Analyze。
Autovacuum 未运行:Autovacuum 是一个进程守护程序,可以自动执行 VACUUM 和 ANALYZE 命令。Autovacuum 会检查数据库中是否存在膨胀的表,并回收空间以供重复使用。Autovacuum 进程守护程序通过在设置的元组阈值失效时运行 Analyze 操作来确保定期更新表统计信息。这让查询规划器能够使用基于最新统计信息的最高效查询计划。如果 autovacuum 未运行,则查询规划器可能会创建次优查询计划,从而导致查询消耗更多的资源。有关更多信息,请参阅以下内容:
运行以下查询以获取有关上次在表上运行 autovacuum 和 autoanalyze 的时间的信息:
SELECT relname, last_autovacuum, last_autoanalyze FROM pg_stat_user_tables;
主要版本升级后未运行 Analyze:PostgreSQL 数据库通常会在任何主要引擎版本升级后遇到性能问题。出现这些问题的一个常见原因是,升级后未执行 Analyze 操作来刷新 pg_statistic 表。请务必对 RDS for PostgreSQL 数据库实例中的每个数据库运行 Analyze 操作。在主要版本升级期间,不会传输优化器统计信息。因此,必须重新生成所有统计信息,以避免由于资源利用率较高而导致的性能问题。
在主要版本升级后,不带任何参数运行以下命令,为当前数据库中的所有常规表生成统计信息:
ANALYZE VERBOSE
PostgreSQL 日志记录参数
使用 Amazon RDS for PostgreSQL 启用查询日志记录。然后,检查 PostgreSQL 错误日志,确认您的 log_min_duration_statement 和 log_statement 参数已设置为适当的值。有关更多信息,请参阅有关错误报告和日志记录的 PostgreSQL 文档。
降低 CPU 利用率
确定导致高 CPU 利用率的查询后,可以使用以下方法进一步降低 CPU 利用率:
- 若有机会进行调整,请使用 EXPLAIN 和 EXPLAIN ANALYZE 来识别警告。有关更多信息,请参阅有关 EXPLAIN 的 PostgreSQL 文档。
- 如果有重复运行的查询,请考虑使用预定义语句来降低 CPU 的压力。重复运行预定义语句会缓存查询计划。因此,由于计划已在缓存中,所以规划的时间对于后续运行来说要少得多。