PostgreSQL 故障排查:紧急排查与 SQL 熔断处理(CPU 占用 100% 等情况)

文章目录

    • 一、典型故障现象与影响
      • [1. 常见表现](#1. 常见表现)
      • [2. 根本原因分类](#2. 根本原因分类)
    • [二、紧急排查 SOP:5 分钟定位罪魁祸首](#二、紧急排查 SOP:5 分钟定位罪魁祸首)
      • [步骤 1:确认数据库进程状态(10 秒)](#步骤 1:确认数据库进程状态(10 秒))
      • [步骤 2:登录数据库,查看活跃会话(30 秒)](#步骤 2:登录数据库,查看活跃会话(30 秒))
      • [步骤 3:关联操作系统 PID 与数据库会话(关键!)](#步骤 3:关联操作系统 PID 与数据库会话(关键!))
      • [步骤 4:分析嫌疑 SQL 的执行计划(1--2 分钟)](#步骤 4:分析嫌疑 SQL 的执行计划(1–2 分钟))
      • [步骤 5:检查锁阻塞链(若存在等待)](#步骤 5:检查锁阻塞链(若存在等待))
    • [三、SQL 熔断:快速终止问题会话](#三、SQL 熔断:快速终止问题会话)
      • [1. 终止单个会话](#1. 终止单个会话)
      • [2. 批量终止同类会话](#2. 批量终止同类会话)
      • [3. 按用户/应用熔断](#3. 按用户/应用熔断)
    • 四、资源限制:防止问题复发(临时防护)
      • [1. 设置语句超时(statement_timeout)](#1. 设置语句超时(statement_timeout))
      • [2. 设置事务超时(idle_in_transaction_session_timeout)](#2. 设置事务超时(idle_in_transaction_session_timeout))
      • [3. 限制单用户连接数](#3. 限制单用户连接数)
      • [4. 使用 Resource Queue(PostgreSQL 15+)](#4. 使用 Resource Queue(PostgreSQL 15+))
    • [五、高级熔断:基于 pgBouncer 的应用层拦截](#五、高级熔断:基于 pgBouncer 的应用层拦截)
      • [1. pgBouncer 黑名单(通过 HUP 重载)](#1. pgBouncer 黑名单(通过 HUP 重载))
      • [2. 动态暂停数据库](#2. 动态暂停数据库)
    • 六、根因分析与长期治理
    • 七、附录:紧急命令速查表

在生产环境中,PostgreSQL 数据库突发 CPU 占用 100%、连接堆积、响应超时 等故障,往往意味着系统正遭受"毒瘤 SQL"的冲击。这类问题若不及时干预,轻则服务降级,重则数据库雪崩、业务中断。此时,DBA 需要一套 快速定位、精准熔断、临时恢复、根因分析 的标准化应急流程。

本文将围绕 "高 CPU + 慢查询 + 资源耗尽" 这一典型故障场景,系统性地阐述 PostgreSQL 的 紧急排查 SOP(标准操作流程)SQL 熔断机制,涵盖实时诊断、会话终止、资源限制、预防策略四大维度,助你在 5 分钟内控制局面,30 分钟内恢复服务。


一、典型故障现象与影响

1. 常见表现

  • tophtop 显示 postgres 进程 CPU 使用率持续 ≥90%;
  • 应用大量报错:Query execution time exceededConnection timeout
  • 新连接被拒绝:FATAL: sorry, too many clients already
  • pg_stat_activity 中大量 activeidle in transaction 会话;
  • 系统负载(load average)飙升,I/O wait 可能同步升高。

2. 根本原因分类

类型 特征 常见场景
失控的复杂查询 单条 SQL 消耗大量 CPU(如全表 JOIN、无索引排序) 报表、BI 工具、未审核的 ad-hoc 查询
高频低效查询 简单 SQL 被高频调用(如循环内查 DB) 应用逻辑缺陷、缓存穿透
长事务阻塞 事务长时间未提交,持有锁或膨胀 MVCC 快照 手动 BEGIN 后忘记 COMMIT、应用异常未回滚
死锁/锁等待风暴 多事务相互阻塞,CPU 消耗在锁管理上 更新顺序不一致、热点行竞争
统计信息过期 优化器选择错误执行计划(如该走索引却 Seq Scan) 大批量数据导入后未 ANALYZE

⚠️ 注意:CPU 100% 不一定是计算密集型查询------也可能是大量轻量查询并发执行导致上下文切换开销剧增。


二、紧急排查 SOP:5 分钟定位罪魁祸首

步骤 1:确认数据库进程状态(10 秒)

bash 复制代码
# 查看 CPU 占用最高的 postgres 进程
top -c -p $(pgrep -d',' -f "postgres")

# 或使用 htop(更直观)
htop

记录 PID(如 12345),用于后续关联。


步骤 2:登录数据库,查看活跃会话(30 秒)

sql 复制代码
-- 查看所有非空闲会话,按 CPU 时间/运行时间排序
SELECT 
  pid,
  usename,
  application_name,
  client_addr,
  backend_start,
  query_start,
  now() - query_start AS query_duration,
  state,
  wait_event_type,
  wait_event,
  query
FROM pg_stat_activity
WHERE state <> 'idle'
  AND pid <> pg_backend_pid()  -- 排除当前会话
ORDER BY query_duration DESC;

重点关注:

  • query_duration:运行时间最长的查询;
  • state = 'active':正在执行;
  • state = 'idle in transaction':危险!长事务;
  • wait_event:若为 Lock,说明被阻塞。

💡 若无法登录(连接池满),可使用 本地 Unix Socket + 超级用户 强制连接:

bash 复制代码
psql -U postgres -h /var/run/postgresql -d your_db

步骤 3:关联操作系统 PID 与数据库会话(关键!)

PostgreSQL 的每个后端进程 PID 与 pg_stat_activity.pid 一致。

sql 复制代码
-- 假设 top 中看到 PID=12345 占用高 CPU
SELECT pid, query, query_start, state 
FROM pg_stat_activity 
WHERE pid = 12345;

若返回结果,则该 SQL 即为嫌疑对象。

若无结果,可能是后台进程(如 autovacuum、checkpointer),需另查。


步骤 4:分析嫌疑 SQL 的执行计划(1--2 分钟)

对嫌疑 SQL 执行 轻量级 EXPLAIN(避免加重负载):

sql 复制代码
-- 仅估算,不实际执行(安全!)
EXPLAIN (VERBOSE, BUFFERS) <嫌疑SQL>;

关注:

  • 是否出现 Seq Scan on large table
  • 是否有 Nested Loop with huge rows
  • 是否缺少 Index Scan

⚠️ 切勿直接使用 EXPLAIN ANALYZE!它会真实执行 DML,可能雪上加霜。


步骤 5:检查锁阻塞链(若存在等待)

sql 复制代码
-- 查看谁在阻塞谁
SELECT 
  blocked.pid AS blocked_pid,
  blocked.query AS blocked_query,
  blocking.pid AS blocking_pid,
  blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking 
  ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event IS NOT NULL;

若发现 双向阻塞,极可能是死锁前兆。


三、SQL 熔断:快速终止问题会话

定位到问题 SQL 后,立即采取熔断措施。

1. 终止单个会话

sql 复制代码
-- 温和取消(发送 SIGINT,允许回滚)
SELECT pg_cancel_backend(12345);

-- 强制终止(发送 SIGTERM,立即断开)
SELECT pg_terminate_backend(12345);

优先使用 pg_cancel_backend,避免事务残留。

2. 批量终止同类会话

若多个会话执行相同 SQL(如应用 bug 导致循环查询):

sql 复制代码
-- 终止所有执行某类查询的会话
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE query LIKE '%SELECT * FROM huge_table%';

3. 按用户/应用熔断

sql 复制代码
-- 熔断整个应用(谨慎!)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE application_name = 'risky_report_app';

四、资源限制:防止问题复发(临时防护)

在根因修复前,可通过资源限制防止再次爆发。

1. 设置语句超时(statement_timeout)

sql 复制代码
-- 会话级(推荐)
SET statement_timeout = '5s';

-- 全局(postgresql.conf)
statement_timeout = 5s

所有查询超过 5 秒自动取消。

2. 设置事务超时(idle_in_transaction_session_timeout)

防止长事务:

conf 复制代码
idle_in_transaction_session_timeout = 30s

事务中空闲超过 30 秒自动断开。

3. 限制单用户连接数

sql 复制代码
-- 限制某用户最多 10 个连接
ALTER USER risky_user CONNECTION LIMIT 10;

4. 使用 Resource Queue(PostgreSQL 15+)

PostgreSQL 15 引入 Resource Groups(需编译时启用),可限制 CPU/内存配额:

sql 复制代码
CREATE RESOURCE GROUP web_users 
  WITH (cpu_rate_limit=0.6, memory_limit='4GB');

ALTER ROLE app_user SET RESOURCE GROUP web_users;

注:此功能默认未启用,需从源码编译或使用 EnterpriseDB 等发行版。


五、高级熔断:基于 pgBouncer 的应用层拦截

若问题来自特定应用,可在连接池层熔断。

1. pgBouncer 黑名单(通过 HUP 重载)

编辑 pgbouncer.ini

ini 复制代码
; 临时禁止某应用连接
ignore_appname = risky_app

重载配置:

bash 复制代码
kill -HUP $(cat /var/run/pgbouncer/pgbouncer.pid)

2. 动态暂停数据库

sql 复制代码
-- 在 pgBouncer 管理接口执行
PAUSE mydb;
-- 所有新连接排队,现有连接继续
RESUME mydb; -- 恢复

六、根因分析与长期治理

1. 事后复盘 checklist

  • 问题 SQL 是否缺失索引?
  • 是否可拆分为分页查询?
  • 应用是否有缓存?是否缓存穿透?
  • 是否应走只读副本而非主库?
  • 是否需加入 SQL 审核流程?

2. 建立预防机制

(1)上线前 SQL 审核
  • 强制要求 EXPLAIN 计划评审;
  • 使用工具(如 sqlcheck、SonarQube)检测反模式。
(2)监控告警
  • Prometheus 监控 pg_stat_statements.mean_exec_time
  • 告警规则:rate(pg_stat_statements_total_time[5m]) > 阈值
(3)自动熔断(进阶)
  • 使用 ProxySQL自研中间件 ,实现:
    • 单 SQL QPS > 100 自动限流;
    • 平均延迟 > 1s 自动熔断。

七、附录:紧急命令速查表

场景 命令
查看高 CPU 进程 top -c -p $(pgrep -d',' -f "postgres")
登录超级用户 psql -U postgres -h /var/run/postgresql
查活跃会话 SELECT ... FROM pg_stat_activity WHERE state <> 'idle';
终止单个会话 SELECT pg_terminate_backend(12345);
批量终止 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE query LIKE '%...%';
设置超时 SET statement_timeout = '5s';
查锁阻塞 SELECT ... FROM pg_stat_activity blocked JOIN ... pg_blocking_pids...
重置统计 SELECT pg_stat_statements_reset();

结语:面对 PostgreSQL CPU 100% 等紧急故障,速度决定损失。通过本文所述 SOP:

  1. 5 分钟内:定位高 CPU 进程 → 关联 SQL → 分析执行计划;
  2. 10 分钟内:熔断问题会话,恢复服务可用性;
  3. 30 分钟内:设置资源限制,防止二次爆发;
  4. 事后:根因治理,建立预防机制。

记住:最好的熔断,是让问题 SQL 根本无法上线。将性能意识融入开发流程,才是长久之计。

相关推荐
憧憬成为web高手1 小时前
皮卡丘靶场速通--sql 2
数据库·sql·mybatis
段一凡-华北理工大学1 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
oradh3 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
z123456789864 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD4 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward5 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记5 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发6 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB6 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
欲速不达88206 小时前
Mysql并发DML锁等待的优化
数据库·mysql