优化实例中的高 CPU 使用率

高 CPU 利用率会对实例的性能产生负面影响。对实例执行的任何活动都使用 CPU。因此,如果存在高 CPU 利用率通知,则应首先确定问题的根本原因,如查询性能不佳、长时间运行的事务或任何其他数据库活动。

本文档介绍了确定实例中 CPU 瓶颈和缓解实例中 CPU 利用率问题的方法。

识别 CPU 瓶颈

使用 Query Insights 来识别 CPU 耗用量较高的查询

Query Insights 可帮助您检测、诊断和避免 Cloud SQL 数据库的查询性能问题。

使用 pg_proctab 扩展程序

pg_proctab 扩展程序与 pg_top 实用程序结合使用,以获取提供每个进程 CPU 利用率信息的操作系统输出。

使用查询

按状态标识活跃连接

与数据库的每个活跃连接都会占用一定数量的 CPU,因此如果实例具有大量连接,则累计利用率可能较高。使用以下查询可获取有关状态的连接数的相关信息。

SELECT
  state,
  usename,
  count(1)
FROM
  pg_stat_activity
WHERE
  pid <> pg_backend_pid()
group by
  state,
  usename
order by
  1;

输出类似于以下内容:


        state        |    usename    | count
---------------------+---------------+-------
 active              | ltest         |   318
 active              | sbtest        |    95
 active              |               |     2
 idle                | cloudsqladmin |     2
 idle in transaction | ltest         |    32
 idle in transaction | sbtest        |     5
                     | cloudsqladmin |     3
                     |               |     4
(8 rows)

如果活跃连接数较高,请检查是否存在长时间运行的查询或阻止查询执行的等待事件。

如果空闲连接计数较高,请执行以下查询,在获得必要的批准后终止连接。

SELECT
  pg_terminate_backend(pid)
FROM
  pg_stat_activity
WHERE
  usename = 'sbtest'
  and pid <> pg_backend_pid()
  and state in ('idle');

您还可以使用以下查询通过 pg_terminate_backend 单独终止连接:

SELECT pg_terminate_backend (<pid>);

在这里,您可以从 pg_stat_activity 获取 PID。

确定长时间运行的连接

下面是一个返回长时间运行的查询的查询示例。在此示例中,您可以识别活跃时间超过 5 分钟的查询。

SELECT
  pid,
  query_start,
  xact_start,
  now() - pg_stat_activity.query_start AS duration,
  query,
  state
FROM
  pg_stat_activity
WHERE
  (
    now() - pg_stat_activity.query_start
  ) > interval '5 minutes' order by 4 desc;

查看说明计划,找出编写不佳的查询

使用说明计划调查编写不佳的查询,并根据需要重写查询。(可选)考虑在必要的批准下使用以下命令取消长时间运行的查询。

SELECT pg_cancel_backend(<pid>);

监控 VACUUM 活动

清除死元组的 AUTOVACUUM 活动是一项 CPU 密集型操作。如果您的实例使用 PostgreSQL 版本 11 或更高版本,请使用以下查询来检查是否存在任何正在进行的 AUTOVACUUM 或 VACUUM 活动。

SELECT
  relid :: regclass,
  pid,
  phase,
  heap_blks_total,
  heap_blks_scanned,
  heap_blks_vacuumed,
  index_vacuum_count,
  max_dead_tuples,
  num_dead_tuples
FROM
  pg_stat_progress_vacuum;

使用以下查询检查实例中是否有正在进行的 VACUUM 活动:

SELECT
  pid,
  datname,
  usename,
  query
FROM
  pg_stat_activity
WHERE
  query like '%vacuum%';

此外,您还可以优化 PostgreSQL 中的 VACUUM 操作并排查问题

添加 pg_stat_statements 扩展程序

设置 pg_stat_statements 扩展程序以获取关于实例活动的增强字典信息。

频繁的检查点

频繁的检查点会降低性能。如果 PostgreSQL 提醒日志报告了 checkpoint occurring too frequently 警告,请考虑调整 checkpoint_timeout 标志。

收集统计信息

请确保查询规划器具有有关表的最新统计信息,以便为查询选择最佳计划。ANALYZE 操作会收集有关数据库中表内容的统计信息,并将结果存储在 pg_statmatic 系统目录中。随后,查询规划器会使用这些统计信息来帮助确定查询的最有效执行计划。AUTOVACUUM 流程会定期自动分析表,因此运行以下命令可检查所有表是否均已分析,以及是否具有可用于规划器的最新元数据。

SELECT
  relname,
  last_autovacuum,
  last_autoanalyze
FROM
  pg_stat_user_tables;

系统设置不足

查询性能也会受到其他因素、标志设置或系统因素的影响。运行以下查询可检查等待事件和等待事件类型,以深入了解其他系统设置的性能。

SELECT
  datname,
  usename,
  (
    case when usename is not null then state else query end
  ) AS what,
  wait_event_type,
  wait_event,
  backend_type,
  count(*)
FROM
  pg_stat_activity
GROUP BY
  1,
  2,
  3,
  4,
  5,
  6
ORDER BY
  1,
  2,
  3,
  4 nulls first,
  5,
  6;

输出类似于以下内容:


 ..  | .. | what           | wait_event_type |      wait_event      | ..    | count
-..--+-..-+----------------+-----------------+----------------------+-..----+------
 ..
 ..  | .. | active         | IO              | CommitWaitFlush      | ..    |   750
 ..  | .. | idle           | IO              | CommitWaitFlush      | ..    |   360
 ..  | .. | active         | LWLock          | BufferMapping        | ..    |   191

监控顺序扫描

对超过几十行的表进行频繁的顺序扫描通常表明缺少索引。如果扫描涉及数千行甚至数十万行,则可能会导致 CPU 利用率过高。

对包含数十万行的表进行频繁的顺序扫描可能会导致 CPU 使用率过高。通过创建必要的索引,避免对此类表执行顺序扫描。

运行以下查询以检查对任何表启动顺序扫描的次数。

SELECT
  relname,
  idx_scan,
  seq_scan,
  n_live_tup
FROM
  pg_stat_user_tables
WHERE
  seq_scan > 0
ORDER BY
  n_live_tup desc;

最后,如果 CPU 仍然较高,并且您认为这些查询是合法流量,请考虑增加实例中的 CPU 资源,以避免数据库崩溃或停机。