Skip to content

PostgreSQL 慢查询风暴问题

慢查询风暴现象

1. 系统层面

  • CPU使用率激增:数据库服务器CPU使用率达到100%
  • 内存使用率上升:大量内存被用于查询执行
  • 磁盘I/O增加:大量的磁盘读取操作
  • 网络流量增加:大量查询结果返回

2. 数据库层面

  • 查询响应时间变长:正常查询的响应时间从毫秒级变为秒级或更长
  • 连接数增加:大量查询堆积,占用连接资源
  • 锁等待增加:查询之间的锁竞争加剧
  • 错误信息
    • WARNING: query is slow: 10000ms
    • ERROR: canceling statement due to statement timeout

慢查询风暴诊断

1. 查看当前活跃查询

sql
-- 查看当前活跃查询
SELECT pid, usename, application_name, client_addr, query_start, now() - query_start AS duration, state, query
FROM pg_stat_activity
WHERE state = 'active' 
ORDER BY duration DESC;

-- 查看长时间运行的查询
SELECT pid, usename, query_start, now() - query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - query_start > interval '5 minutes';

2. 分析慢查询日志

bash
# 查看慢查询日志配置
psql -c "SHOW log_min_duration_statement;"
psql -c "SHOW log_directory;"

# 搜索慢查询日志
LOG_DIR=$(psql -t -c "SHOW log_directory;")
grep -i "duration:.*ms" $LOG_DIR/*.log | sort -nr -k 3 | head -n 20

# 使用pgBadger分析慢查询日志
pgbadger $LOG_DIR/*.log -o slow_query_report.html

3. 使用pg_stat_statements分析

sql
-- 查看Top 10慢查询
SELECT queryid, query, calls, total_time, mean_time, rows
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;

-- 查看执行次数最多的查询
SELECT queryid, query, calls, total_time, mean_time, rows
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 10;

-- 查看总耗时最多的查询
SELECT queryid, query, calls, total_time, mean_time, rows
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;

4. 分析查询计划

sql
-- 使用EXPLAIN ANALYZE分析慢查询
EXPLAIN ANALYZE SELECT * FROM users WHERE name LIKE '%test%';

-- 使用EXPLAIN (BUFFERS, ANALYZE)获取更详细的信息
EXPLAIN (BUFFERS, ANALYZE) SELECT * FROM orders WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';

慢查询风暴解决措施

1. 紧急处理

终止慢查询

sql
-- 终止单个慢查询
SELECT pg_cancel_backend(pid)
FROM pg_stat_activity
WHERE pid = 12345;

-- 终止所有慢查询(谨慎使用)
SELECT pg_cancel_backend(pid)
FROM pg_stat_activity
WHERE state = 'active' AND now() - query_start > interval '5 minutes';

-- 强制终止查询(如果pg_cancel_backend无效)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid = 12345;

限制查询资源

sql
-- 设置查询超时
ALTER SYSTEM SET statement_timeout = '30s';
SELECT pg_reload_conf();

-- 限制单个用户的连接数
ALTER ROLE app_user CONNECTION LIMIT 10;

-- 限制单个用户的查询速率(使用pg_stat_accounting扩展)
-- 需要先安装扩展
CREATE EXTENSION pg_stat_accounting;

2. 根本解决方案

优化查询

sql
-- 为慢查询添加索引
CREATE INDEX idx_users_name ON users(name);

-- 优化查询语句
-- 原查询:
SELECT * FROM users WHERE name LIKE '%test%';

-- 优化后:
-- 1. 如果只需要特定列,不要使用SELECT *
SELECT id, name FROM users WHERE name LIKE '%test%';

-- 2. 如果可能,避免使用前置通配符
SELECT id, name FROM users WHERE name LIKE 'test%';

-- 3. 使用更高效的查询方式
SELECT id, name FROM users WHERE position('test' IN name) > 0;

调整配置参数

sql
-- 调整work_mem参数
ALTER SYSTEM SET work_mem = '16MB';

-- 调整shared_buffers参数
ALTER SYSTEM SET shared_buffers = '4GB';

-- 调整effective_cache_size参数
ALTER SYSTEM SET effective_cache_size = '12GB';

-- 重新加载配置
SELECT pg_reload_conf();

优化数据库设计

  • 规范化设计:确保数据库设计符合规范化原则
  • 分区表:对大表使用分区,提高查询性能
  • 垂直拆分:将大表拆分为多个小表,减少I/O
  • 水平拆分:将数据按照某种规则分布到多个表中

慢查询风暴监控与告警

1. 实时监控

sql
-- 监控当前慢查询数量
SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '10s';

-- 监控CPU使用率
SELECT 
  round(100.0 * (SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active') / (SELECT setting::int FROM pg_settings WHERE name = 'max_connections'), 2) AS active_connection_percentage,
  (SELECT 100.0 * (total_time - blks_hit * 8192 / 1024 / 1024) / total_time FROM pg_stat_database WHERE datname = current_database()) AS disk_io_percentage;

2. 慢查询日志配置

sql
-- 配置慢查询日志
ALTER SYSTEM SET log_min_duration_statement = '1000'; -- 记录超过1秒的查询
ALTER SYSTEM SET log_statement = 'mod'; -- 记录数据修改语句
ALTER SYSTEM SET log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ';
ALTER SYSTEM SET log_temp_files = '0'; -- 记录所有临时文件

-- 重新加载配置
SELECT pg_reload_conf();

3. 告警配置

Prometheus + Grafana监控

  • 监控指标

    • pg_stat_statements_mean_time:平均查询时间
    • pg_stat_statements_calls:查询执行次数
    • pg_stat_activity_count_by_state:按状态分类的连接数
    • node_cpu_seconds_total:CPU使用率
  • 告警规则

    • 慢查询数量超过10个时告警
    • 平均查询时间超过5秒时告警
    • CPU使用率超过90%时告警

Zabbix监控

  • 监控项

    • PostgreSQL慢查询数量
    • PostgreSQL平均查询时间
    • PostgreSQL CPU使用率
  • 触发器

    • 慢查询数量 > 10
    • 平均查询时间 > 5秒
    • CPU使用率 > 90%

慢查询风暴预防措施

1. 开发阶段

  • 查询性能测试:在开发阶段进行查询性能测试
  • 索引设计:根据查询需求设计合理的索引
  • 查询优化:编写高效的SQL查询
  • 代码审查:对数据库相关代码进行审查

2. 测试阶段

  • 压力测试:模拟高并发场景进行压力测试
  • 性能基准测试:建立性能基准,定期进行测试
  • 慢查询监控:在测试环境中监控慢查询

3. 生产环境

  • 定期分析慢查询:每周或每月分析慢查询日志
  • 自动优化:使用pg_stat_statements定期分析并优化慢查询
  • 配置合理的超时时间:设置statement_timeout参数
  • 限制查询资源:限制单个用户的连接数和查询速率

慢查询风暴最佳实践

1. 紧急响应流程

  1. 发现问题:通过监控系统发现慢查询风暴
  2. 初步诊断:查看当前活跃查询,确定慢查询类型
  3. 紧急处理:终止长时间运行的查询,限制查询资源
  4. 根本分析:分析慢查询日志和查询计划,确定根本原因
  5. 优化修复:优化查询、添加索引、调整配置
  6. 验证效果:监控系统状态,验证修复效果
  7. 预防措施:制定预防类似问题的措施

2. 长期优化策略

  • 建立慢查询监控体系:实时监控慢查询数量和性能
  • 定期进行性能优化:每月进行一次数据库性能优化
  • 优化数据库配置:根据业务需求调整数据库配置
  • 培训开发人员:提高开发人员的SQL编写能力
  • 建立性能基准:定期进行性能基准测试

常见问题(FAQ)

Q1:什么是慢查询风暴?

A1:慢查询风暴是指数据库中突然出现大量慢查询,导致数据库性能急剧下降,甚至无法提供服务的现象。慢查询风暴通常是由于某个或某些查询执行效率低下,在高并发场景下被大量执行,占用了大量的系统资源。

Q2:慢查询风暴的常见原因有哪些?

A2:慢查询风暴的常见原因包括:

  • 缺少必要的索引
  • SQL查询编写低效
  • 数据库配置不当
  • 数据量突增
  • 锁竞争加剧
  • 硬件资源不足

Q3:如何快速处理慢查询风暴?

A3:快速处理慢查询风暴的方法包括:

  • 终止长时间运行的查询
  • 限制查询资源,如设置查询超时
  • 限制单个用户的连接数
  • 临时增加系统资源

Q4:如何预防慢查询风暴?

A4:预防慢查询风暴的措施包括:

  • 建立慢查询监控体系
  • 定期分析和优化慢查询
  • 为查询添加必要的索引
  • 优化数据库配置
  • 进行压力测试和性能基准测试
  • 培训开发人员编写高效的SQL

Q5:如何使用pg_stat_statements分析慢查询?

A5:可以使用以下步骤使用pg_stat_statements分析慢查询:

  1. 安装pg_stat_statements扩展
  2. 配置shared_preload_libraries参数
  3. 重启PostgreSQL服务
  4. 使用pg_stat_statements视图查询慢查询信息
  5. 根据查询信息优化慢查询

Q6:如何配置慢查询日志?

A6:可以使用以下步骤配置慢查询日志:

  1. 设置log_min_duration_statement参数,指定慢查询的阈值
  2. 设置log_statement参数,指定需要记录的语句类型
  3. 设置log_line_prefix参数,指定日志格式
  4. 设置log_temp_files参数,记录临时文件
  5. 重新加载配置

Q7:如何优化慢查询?

A7:优化慢查询的方法包括:

  1. 为查询添加必要的索引
  2. 优化SQL查询语句,避免使用低效的查询方式
  3. 调整数据库配置参数
  4. 优化数据库设计,如使用分区表
  5. 增加系统资源,如CPU、内存、磁盘等