优化实例中的高 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_statistic 系统目录中。随后,查询规划器会使用这些统计信息来帮助确定最有效的查询执行计划。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 资源,以避免数据库崩溃或停机。