数据库的CPU使用率突然冲高时,通常是由于SQL执行效率低下、锁竞争、资源争用或系统配置不当导致。以下是排查和优化的系统性方法:
1. 快速定位当前高负载SQL
1.1 查看正在运行的SQL
-- MySQL
SHOW FULL PROCESSLIST;
-- 按执行时间排序(重点关注State为"Sending data"、"Sorting result"的查询)
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE COMMAND != 'Sleep'
ORDER BY TIME DESC;
-- Oracle
SELECT s.sid, s.serial#, s.sql_id, s.status, s.machine, s.program, sq.sql_text
FROM v$session s
JOIN v$sql sq ON s.sql_id = sq.sql_id
WHERE s.status = 'ACTIVE';
1.2 分析慢查询日志
- 开启慢查询日志(MySQL):
- VARIABLES LIKE 'log_error'; //查看mysql数据库日志路径
-
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2; -- 记录执行超过2秒的查询
- 分析日志文件(使用工具如
mysqldumpslow
或pt-query-digest
):pt-query-digest /var/lib/mysql/slow.log
1.3 实时抓取高CPU消耗的SQL
- 使用
performance_schema
(MySQL 5.6+):-- 查看当前线程消耗的CPU时间 SELECT THREAD_ID, EVENT_NAME, SUM_TIMER_WAIT/1e9 AS CPU_SECONDS FROM performance_schema.events_events_statements_summary_by_thread_by_event_name WHERE EVENT_NAME LIKE 'statement/sql/%' ORDER BY CPU_SECONDS DESC LIMIT 10;
2. 分析SQL性能问题
2.1 检查执行计划
对高负载SQL使用 EXPLAIN
或 EXPLAIN ANALYZE
:
EXPLAIN FORMAT=JSON
SELECT * FROM orders WHERE user_id = 1000 AND status = 'PAID';
- 关键指标:
type
是否为全表扫描(ALL)?rows
是否远大于实际返回行数?Extra
是否出现Using filesort
或Using temporary
?
2.2 常见低效SQL模式
- 缺失索引:WHERE条件中的字段无索引。
- 全表扫描:大表未命中索引。
- 隐式类型转换:如字符串字段用数字查询(
WHERE id = '100'
)。 - 复杂JOIN:多表关联导致笛卡尔积爆炸。
3. 检查锁与事务竞争
3.1 锁阻塞分析
-
MySQL:
-- 查看当前锁等待 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; -- 查看未提交的长事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX ORDER BY TIME_ELAPSED DESC;
-
Oracle:
-- 查看锁阻塞链 SELECT sid, serial#, blocking_session, sql_id, seconds_in_wait FROM v$session WHERE blocking_session IS NOT NULL;
3.2 事务优化建议
- 避免长事务:拆分大事务,减少锁持有时间。
- 使用乐观锁:通过版本号替代行级锁。
- 索引优化:减少锁范围(如缩小WHERE条件覆盖的数据量)。
4. 系统与硬件资源排查
4.1 监控系统指标
- CPU使用分布:
top -H -p $(pgrep mysqld) # 查看MySQL线程的CPU占用
- I/O瓶颈:
iostat -x 1 # 检查%util和await
4.2 配置调优
- 连接数限制:避免过多连接导致线程争用。
SET GLOBAL max_connections = 500; -- 根据硬件调整
- 缓冲池配置(MySQL InnoDB):
SET GLOBAL innodb_buffer_pool_size = 8G; -- 通常设为物理内存的70%~80%
5. 高级诊断工具
5.1 火焰图(FlameGraph)
- 使用
perf
或pt-pmp
抓取CPU堆栈:perf record -p $(pgrep mysqld) -g -- sleep 30 perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg
- 分析热点:定位到具体的函数或SQL操作。
5.2 数据库性能仪表盘
- Prometheus + Grafana:监控QPS、TPS、锁等待等。
- Percona Monitoring and Management (PMM):一键部署数据库性能监控。
6. 常见原因总结与优化方向
原因分类 | 典型场景 | 优化方案 |
---|---|---|
SQL执行低效 | 全表扫描、缺失索引、复杂子查询 | 添加索引、重写SQL、使用覆盖索引 |
锁竞争 | 行锁等待、未提交事务阻塞其他操作 | 缩短事务时间、异步处理、队列削峰 |
资源不足 | 高并发导致线程争用、缓冲池过小 | 升级CPU/内存、调整innodb_thread_concurrency |
配置错误 | 连接数爆满、日志写入频繁 | 限制连接数、关闭调试日志 |
7. 紧急处理步骤
- 终止高危进程:
-- MySQL KILL [PROCESS_ID]; -- Oracle ALTER SYSTEM KILL SESSION 'sid,serial#';
- 限流降级:通过中间件(如ShardingSphere)限制并发数。
- 快速扩容:临时增加CPU资源(云数据库支持弹性扩容)。
通过以上步骤,可逐步定位到CPU冲高的根本原因。优先从SQL优化和索引调整入手,通常能解决80%的性能问题。