文章目录
一、当你的SQL开始"便秘"时(问题定位)
最近接手一个日活百万的电商系统时(真实案例),突然收到DBA的夺命连环call:“有个订单统计接口把数据库CPU吃满了!!!”。打开慢查询日志一看,一条看似无害的SQL正在疯狂执行:
SELECT * FROM orders
WHERE user_id = 12345
AND create_time > '2023-01-01'
ORDER BY price DESC
LIMIT 1000;
(惊悚时刻)这条查询平均耗时8秒,单日执行20万次!更可怕的是它竟然没有走索引…接下来咱们开启侦探模式:
二、庖丁解牛:慢SQL诊断三板斧
1. EXPLAIN深度解析(必杀技)
EXPLAIN SELECT...
重点看这些字段(划重点):
- type:如果出现ALL(全表扫描),马上拉响警报!
- key:显示实际使用的索引,NULL就是裸奔查询
- rows:预估扫描行数,超过1万就要警惕
- Extra:出现"Using filesort"说明在内存排序,性能杀手
2. 慢查询日志分析(案发现场)
在my.cnf配置:
slow_query_log = 1
long_query_time = 1 # 超过1秒就记录
log_queries_not_using_indexes = 1 # 揪出裸奔查询
3. 性能画像工具(PERFORMANCE_SCHEMA)
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC LIMIT 5;
这个神器能直接定位最耗时的SQL模板
三、实战优化案例:从8秒到0.02秒的蜕变
原始问题分析
-- 耗时8秒的元凶
SELECT * FROM orders
WHERE user_id=12345
AND create_time>'2023-01-01'
ORDER BY price DESC
LIMIT 1000;
EXPLAIN结果:
- type: ALL
- key: NULL
- rows: 500,000
- Extra: Using where; Using filesort
优化第一式:联合索引手术
ALTER TABLE orders ADD INDEX idx_user_create_price(user_id, create_time, price);
(重要原理)最左前缀原则:
- 索引顺序必须遵循查询条件顺序
- 范围查询列要放在最后
优化第二式:分页优化黑科技
SELECT * FROM orders
WHERE user_id=12345
AND create_time>'2023-01-01'
AND price < ? -- 记住上一页最后一条的price值
ORDER BY price DESC
LIMIT 1000;
这样避免深分页导致的性能悬崖
优化第三式:查询改写魔法
SELECT o.* FROM orders o
JOIN (
SELECT id FROM orders
WHERE user_id=12345
AND create_time>'2023-01-01'
ORDER BY price DESC
LIMIT 1000
) tmp ON o.id = tmp.id;
通过子查询先定位ID,再获取数据,减少排序开销
四、进阶优化技巧(压箱底干货)
1. 索引下推(ICP)
MySQL5.6+的隐藏大招,在存储引擎层直接过滤数据
查看开启状态:
SHOW VARIABLES LIKE '%optimizer_switch%';
2. 冷热数据分离
对于create_time这类时间字段,可以按月份分表:
orders_202301
orders_202302
...
3. 巧用覆盖索引
-- 原始查询
SELECT * FROM table WHERE col1=?
-- 优化为
SELECT col1,col2,col3 FROM table WHERE col1=?
当索引包含所有查询字段时,效率直接起飞
五、那些年我们踩过的坑(血泪教训)
1. 索引越多越好?(大错特错!)
某系统建了30多个索引,结果写操作慢了10倍!记住:
- 每个表索引不要超过5个
- 单个索引字段不要超过3列
2. OR条件引发的惨案
WHERE status=1 OR price>100
这种查询会让索引失效,应该改写为:
SELECT ... WHERE status=1
UNION ALL
SELECT ... WHERE price>100 AND status<>1
3. 隐式类型转换陷阱
WHERE user_id = '12345' # user_id是int类型
这个简单的引号会导致全表扫描!
六、性能监控体系搭建(长治久安之道)
1. 实时监控方案
# 每秒刷新查看进程
mysqladmin -uroot -p processlist --sleep 1
2. 慢查询可视化
推荐工具:
- Percona Monitoring and Management
- MySQL Enterprise Monitor
3. 自动化优化建议
使用pt-query-digest分析慢日志:
pt-query-digest slow.log > slow_analysis.log
七、终极优化心法(价值百万的经验)
经过上百个项目的优化实战,我总结出黄金法则:
- 先测量,后优化(不要盲目调优)
- 索引不是银弹(综合考量查询模式)
- 拥抱架构优化(当单机扛不住时)
- 预防胜于治疗(建立SQL审核机制)
最后送大家一个自查清单:
- 所有查询都走了合适的索引吗?
- 有没有出现全表扫描?
- 排序操作是否使用了文件排序?
- 是否存在隐式类型转换?
- 分页查询是否做了优化?
(超级重要)记住:优化是个持续的过程,就像保持身材需要持续锻炼一样!当你搞定一个慢查询时,记得开瓶肥宅快乐水庆祝下,然后继续寻找下一个优化目标~