文章目录
一、问题背景:为什么我的SQL比蜗牛还慢?🐌
最近接手了个用户中心模块的优化需求,系统日志里躺着200+条慢查询(超过2秒的那种)。最夸张的一条统计SQL执行时间高达28秒(生产环境你敢信?!),DBA已经在群里@我三次了(救命🆘)
二、分析工具全家桶(附操作实录)
2.1 Explain工具实战
-- 举个真实案例(表名已脱敏)
EXPLAIN SELECT *
FROM user_behavior
WHERE create_time > '2023-01-01'
AND status = 1
AND device_type IN (2,5,7)
ORDER BY login_count DESC
LIMIT 100;
输出结果关键指标解读:
- type=ALL(全表扫描警告⚠️)
- rows=870000(扫描行数爆炸💥)
- Extra=Using filesort(文件排序警告🚨)
2.2 慢查询日志分析技巧
在my.cnf中开启配置:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
查看TOP 10慢查询:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
三、实战优化五连鞭(真实案例拆解)
案例1:联合索引顺序翻车现场
-- 原索引(坑爹设计)
ALTER TABLE orders ADD INDEX idx_cat_time(category, create_time);
-- 实际查询(效果极差)
SELECT * FROM orders
WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
AND category = '电子产品';
问题诊断:联合索引的首列未被使用,导致索引失效(划重点)
优化方案:
-- 重建索引(正确姿势)
ALTER TABLE orders ADD INDEX idx_time_cat(create_time, category);
-- 查询语句无需修改,性能提升300%↑
案例2:隐式转换引发的血案
-- 用户表user_id是varchar类型
SELECT * FROM user_log
WHERE user_id = 10086
AND log_type = 'login';
问题诊断:user_id的varchar与数字10086比较,引发全表扫描(类型不匹配警告🚩)
优化方案:
-- 改为字符串匹配
SELECT * FROM user_log
WHERE user_id = '10086'
AND log_type = 'login';
-- 执行时间从3.2秒→0.02秒(起死回生🎉)
案例3:分页查询的死亡陷阱
-- 原语句(1000页之后慢到怀疑人生)
SELECT * FROM products
ORDER BY sales DESC
LIMIT 100000, 20;
优化方案:
-- 使用游标分页(性能提升秘籍)
SELECT * FROM products
WHERE id > 100000
ORDER BY sales DESC
LIMIT 20;
案例4:JSON字段查询优化
-- 用户扩展信息存JSON
SELECT * FROM users
WHERE JSON_EXTRACT(ext_info, '$.vip_level') > 3;
问题诊断:每次查询都要解析整个JSON字段(性能杀手🔪)
优化方案:
-- 新增冗余字段
ALTER TABLE users ADD COLUMN vip_level TINYINT;
UPDATE users SET vip_level = JSON_EXTRACT(ext_info, '$.vip_level');
-- 查询速度从1.8秒→0.03秒(真香警告🍚)
案例5:统计查询的救赎之路
-- 原实时统计(每天卡死三次)
SELECT COUNT(*) AS total,
SUM(amount) AS total_amount,
AVG(price) AS avg_price
FROM orders
WHERE status = 4
AND create_time BETWEEN '2023-01-01' AND '2023-12-31';
优化方案:
-- 创建汇总表
CREATE TABLE order_stats_daily (
stat_date DATE PRIMARY KEY,
total_orders INT,
total_amount DECIMAL(18,2),
avg_price DECIMAL(10,2)
);
-- 使用定时任务每日凌晨更新
四、性能对比表(优化前后对比)
案例编号 | 优化前耗时 | 优化后耗时 | 提升倍数 |
---|---|---|---|
案例1 | 4.8s | 0.5s | 9.6× |
案例2 | 3.2s | 0.02s | 160× |
案例3 | 12.4s | 0.3s | 41× |
案例4 | 1.8s | 0.03s | 60× |
案例5 | 8.7s | 0.01s | 870× |
五、血泪教训总结(必看避坑指南)
-
索引不是万金油:维护过期的索引反而会降低写性能(亲身经历索引过多导致写入慢2倍😭)
-
EXPLAIN要看全:有次只看type没注意filesort,结果在500万数据表翻车(被DBA追着打😅)
-
字段类型要规范:曾经把IP存成varchar,后来改成INT用INET_ATON()函数,查询快10倍(真·教科书级优化)
-
定期review慢日志:设置每周一检查慢查询的日历提醒(救过三次线上事故💣)
-
ORM框架要慎用:见过N+1查询搞崩系统的案例(特别是Hibernate的延迟加载陷阱😱)
六、终极武器:SQL优化自检清单
下次写SQL前,先问自己这7个问题:
- 是否使用了合适的索引?
- 有没有隐式类型转换?
- 分页查询是否深度过大?
- 是否可以使用覆盖索引?
- 是否有不必要的ORDER BY?
- 是否返回了过多字段?
- 能否将实时查询改为异步计算?
最后送大家一句话:优化永无止境,但不要为了优化而优化(见过把简单查询复杂化反而更慢的惨案)。祝大家的SQL都能快到飞起!🚀