凌晨三点,报警短信炸醒:MySQL CPU 100%!业务全挂!别慌,看我如何30分钟搞定这个"午夜惊魂"!
事故现场:CPU爆表的惨状
监控告警:
- CPU使用率:100%
- 连接数:爆满
- QPS:断崖式下跌
- 业务接口:全部超时
内心OS: 这要是搞不定,明天就可以卷铺盖走人了!
第0步:别慌,深呼吸 先问自己
- 是突然飙升还是缓慢上涨?
- 业务高峰时段还是平时?
- 最近有没有上线新功能
就像医生看病先问诊,咱也得先了解病情。
第一步:紧急止血(5分钟搞定)
CPU 100%的时候,别想着一步到位解决问题,先让服务活过来最重要。
1.1 快速连接MySQL(30秒)
-- 紧急登录(跳过权限验证)
mysql -h 127.0.0.1 -u root -p --skip-secure-auth
-- 如果连接不上,强制重启(最后手段)
sudo systemctl restart mysql
1.2 查看当前状态
-- 查看当前连接和进程
SHOW PROCESSLIST;
-- 查看全局状态
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Queries';
关键观察点:
- 是否有大量
Sleep连接? - 是否有长时间运行的查询?
- 是否有锁等待?
1.3 紧急止血操作
-- 杀死长时间运行的查询
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep'
AND TIME > 60
ORDER BY TIME DESC;
-- 逐个杀死问题进程
KILL 12345;
KILL 12346;
-- 调整关键参数(临时生效)
SET GLOBAL innodb_buffer_pool_size = 2147483648; -- 2GB
SET GLOBAL tmp_table_size = 268435456; -- 256MB
SET GLOBAL max_heap_table_size = 268435456; -- 256MB
SET GLOBAL thread_cache_size = 16;
-- 清理缓存和临时表
FLUSH TABLES;
FLUSH QUERY CACHE;
RESET QUERY CACHE;
第二步:问题定位(10分钟深度排查)
2.1 实时监控关键指标
-- 查看当前活跃的慢查询
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep'
AND INFO IS NOT NULL
ORDER BY TIME DESC
LIMIT 10;
-- 查看锁等待情况
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
2.2 开启慢查询日志分析
-- 临时开启慢查询日志(不用重启)
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;
SET GLOBAL slow_query_log_file = '/tmp/slow.log';
-- 查看当前慢查询配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
2.3 使用性能分析工具
# 使用mysqldumpslow分析慢日志
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log
# 使用pt-query-digest(更强大)
pt-query-digest /var/lib/mysql/slow.log
# 实时查看服务器状态
mysqladmin -u root -p ext -i 1
第三步:深度排查方向
方向1:SQL查询问题
-- 查看执行次数最多的SQL
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY COUNT_STAR DESC
LIMIT 10;
-- 查看响应时间最长的SQL
SELECT DIGEST_TEXT, AVG_TIMER_WAIT, MAX_TIMER_WAIT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;
-- 分析慢查询日志
mysqldumpslow -s t /var/lib/mysql/slow.log pt-query-digest /var/lib/mysql/slow.log
-- 实时查看慢查询
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;
方向2:索引问题排查
-- 查看没有使用索引的查询
SELECT * FROM sys.schema_unused_indexes;
-- 查看缺失索引的情况
SELECT * FROM sys.schema_index_statistics WHERE INDEX_NAME IS NOT NULL;
SELECT * FROM sys.statements_with_sorting;
-- 查看表扫描情况
SELECT * FROM sys.schema_tables_with_full_table_scans limit 10;
方向3:锁竞争分析
-- 查看当前锁信息
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
-- 查看等待锁的线程
SELECT * FROM sys.innodb_lock_waits;
-- 查看表锁情况
SHOW STATUS LIKE 'Table_locks_%';
方向4:连接数暴增
-- 查看连接来源
SELECT * FROM information_schema.PROCESSLIST
WHERE HOST NOT LIKE '127.0.0.1%'
AND HOST NOT LIKE 'localhost%';
-- 查看最大连接数
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
-- 杀掉异常连接(谨慎使用!)
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.PROCESSLIST
WHERE USER = 'bad_user'
OR TIME > 300;
方向5:配置参数问题
-- 🎯 检查关键配置参数
SHOW VARIABLES WHERE Variable_name IN (
'innodb_buffer_pool_size',
'query_cache_size',
'tmp_table_size',
'max_heap_table_size',
'max_connections'
);
第四步:常见问题及解决方案
问题1:慢查询导致的CPU飙升
症状: 大量SQL执行时间超过1秒
问题SQL:
-- ❌ 慢查询示例
SELECT * FROM orders
WHERE DATE(create_time) = '2024-01-01'
AND status IN (1,2,3,4,5,6,7,8,9,10)
ORDER BY id DESC
LIMIT 1000;
解决方案:
-- 使用EXPLAIN分析问题SQL
EXPLAIN SELECT * FROM orders
WHERE DATE(create_time) = '2024-01-01'
AND status IN (1,2,3,4,5,6,7,8,9,10)
ORDER BY id DESC
LIMIT 1000;
-- 优化方案:添加索引或者改写SQL
-- 索引优化策略
-- 删除冗余索引
SELECT * FROM sys.schema_redundant_indexes;
-- 添加缺失索引
SELECT * FROM sys.schema_index_statistics
WHERE table_schema = 'your_database';
-- 复合索引设计原则
-- 1. 等值查询字段在前
-- 2. 范围查询字段在后
-- 3. 排序字段考虑在内
优化方案:
-- ✅ 优化后
SELECT * FROM orders
WHERE create_time >= '2024-01-01 00:00:00'
AND create_time < '2024-01-02 00:00:00'
AND status BETWEEN 1 AND 10
ORDER BY id DESC
LIMIT 1000;
-- 添加索引
ALTER TABLE orders
ADD INDEX idx_create_time_status (create_time, status);
问题2:全表扫描
症状: 没有使用索引,扫描大量数据
解决方案:
-- 查找需要添加索引的表
SELECT * FROM sys.schema_tables_with_full_table_scans;
-- 为常用查询字段添加索引
ALTER TABLE orders ADD INDEX idx_user_status(user_id, status);
ALTER TABLE products ADD INDEX idx_category_price(category_id, price);
问题3:锁等待和死锁
症状: 大量进程处于"Waiting for table metadata lock"
解决方案:
-- 查看当前锁信息
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
-- 优化事务,减少锁持有时间
-- 将大事务拆分为小事务
START TRANSACTION;
-- 分批处理数据
COMMIT;
问题4:配置不合理
症状: 缓冲区设置过小,频繁磁盘IO
解决方案:
-- 优化InnoDB缓冲池(建议设置为物理内存的70-80%)
SET GLOBAL innodb_buffer_pool_size = 2147483648; -- 2GB
-- 调整连接数
SET GLOBAL max_connections = 500;
-- 调整临时表大小
SET GLOBAL tmp_table_size = 67108864; -- 64MB
SET GLOBAL max_heap_table_size = 67108864;
第五步:实战排查脚本
监控预警:建立防护体系
监控指标清单
| 监控项 | 预警阈值 | 检查频率 |
|---|---|---|
| CPU使用率 | >80% | 1分钟 |
| 连接数 | >max_connections的80% | 1分钟 |
| 慢查询数量 | >10个/分钟 | 5分钟 |
| 锁等待时间 | >30秒 | 1分钟 |
| 缓冲池命中率 | <95% | 5分钟 |
5.1 自动化脚本示例
#!/bin/bash
# MySQL健康检查脚本
CPU_THRESHOLD=80
CONN_THRESHOLD=100
check_mysql_health() {
# 检查CPU
CPU_USAGE=$(top -bn1 | grep "mysql" | head -1 | awk '{print \$9}')
if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) )); then
echo "🚨 CPU使用率过高: $CPU_USAGE%"
# 发送告警...
fi
# 检查连接数
CONN_COUNT=$(mysql -e "SHOW STATUS LIKE 'Threads_connected'" | grep Threads_connected | awk '{print \$2}')
if [ $CONN_COUNT -gt $CONN_THRESHOLD ]; then
echo "🚨 连接数过多: $CONN_COUNT"
# 发送告警...
fi
}
5.2 一键排查脚本
#!/bin/bash
# MySQL CPU 100% 一键排查脚本
echo " 开始MySQL性能问题排查..."
# 检查连接数
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
# 检查慢查询
mysql -u root -p -e "SHOW VARIABLES LIKE 'slow_query_log';"
# 检查当前进程
mysql -u root -p -e "SHOW PROCESSLIST;"
# 检查锁等待
mysql -u root -p -e "SELECT * FROM information_schema.INNODB_LOCK_WAITS;"
echo " 排查完成!"
5.3 实时监控脚本
#!/bin/bash
# 实时监控MySQL状态
while true; do
clear
echo "$(date) - MySQL状态监控"
echo "========================"
# 查看连接数
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';" 2>/dev/null
# 查看当前查询
mysql -u root -p -e "SHOW PROCESSLIST;" 2>/dev/null | head -20
sleep 2
done
预防措施:避免再次发生
开发规范
-- 必须使用EXPLAIN分析查询
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- 禁止SELECT *,明确字段
SELECT id, name, email FROM users;
-- 大数据量使用分页
SELECT * FROM orders LIMIT 100 OFFSET 0;
-- 事务要短小精悍
START TRANSACTION;
-- 快速操作...
COMMIT;
运维规范
-- ✅ 定期分析表
ANALYZE TABLE important_table;
-- ✅ 定期优化表
OPTIMIZE TABLE fragmented_table;
-- ✅ 监控索引使用情况
SELECT * FROM sys.schema_index_statistics;
-- ✅ 定期清理历史数据
DELETE FROM log_table WHERE create_time < DATE_SUB(NOW(), INTERVAL 30 DAY);
血泪教训
- 不要相信"这个查询很快" - 在小数据量下跑得快,到生产环境可能就跪了
- 索引不是越多越好 - 就像吃药,要对症下药
- 监控比救火重要 - 等CPU 100%再处理,黄花菜都凉了
- 测试数据要真实 - 用几十条数据测试,上线后面对几百万数据直接傻眼
性能调优参数参考
# my.cnf 关键配置
[mysqld]
# 内存相关
innodb_buffer_pool_size = 4G
innodb_log_file_size = 1G
tmp_table_size = 256M
max_heap_table_size = 256M
# 连接相关
max_connections = 200
thread_cache_size = 16
# 查询相关
query_cache_type = 1
query_cache_size = 128M
long_query_time = 2
slow_query_log = 1
# InnoDB相关
innodb_flush_log_at_trx_commit = 2
innodb_lock_wait_timeout = 50
总结
MySQL CPU 100%排查黄金法则:
- 立即行动 →
SHOW PROCESSLIST快速定位 - 精准打击 → 分析慢查询、索引、锁竞争
- 紧急救援 → 终止问题查询、调整参数
- 根治问题 → SQL优化、索引重构、架构升级
- 建立防护 → 监控预警、开发规范、定期维护
记住这个排查路径:
紧急响应 → 深度排查 → 立即解决 → 根治问题 → 预防再发
从此告别:
- 半夜被报警吵醒
- 手忙脚乱救火
- 业务长时间中断