文章目录
一、被慢查询支配的恐惧
上周三凌晨2点(别问我为什么这个时间还在加班),我们的电商平台突然收到大量用户投诉:“购物车加载不出来!!” 我盯着监控大屏上持续飙红的数据库CPU曲线(已经冲到95%了!),手抖着打开了慢查询日志…
(真实案例警告)这个原本运行良好的订单查询接口,在促销期间响应时间从200ms暴涨到20秒!更可怕的是,这样的慢SQL在日志里像病毒一样疯狂复制,每秒钟触发上百次!
二、破案三板斧
1. 揪出真凶
-- 原始慢SQL
SELECT * FROM orders
WHERE user_id = 12345
AND create_time > '2023-01-01'
AND status IN (1,2,5)
ORDER BY update_time DESC
LIMIT 10 OFFSET 0;
2. EXPLAIN大法好
执行计划显示:
- 全表扫描(type=ALL)!!!
- Using filesort(文件排序警告)
- 扫描行数:2,348,761 行(窒息)
3. 慢查分析四象限
(掏出祖传分析模板)
1. 是否走索引? ❌
2. 是否合理使用索引? ❌
3. 是否存在不必要的数据? ✅
4. 执行计划是否最优? ❌
三、优化实战六连击
1. 索引手术刀
-- 创建复合索引(划重点)
ALTER TABLE orders ADD INDEX idx_query_opt (
user_id,
status,
create_time,
update_time
);
避坑指南:
- 把等值查询条件放最前(user_id、status)
- 范围查询字段置后(create_time)
- 排序字段收尾(update_time)
2. 改写SQL语句
-- 优化后版本
SELECT id, order_no, total_amount
FROM orders FORCE INDEX (idx_query_opt)
WHERE user_id = 12345
AND status IN (1,2,5)
AND create_time > '2023-01-01'
ORDER BY update_time DESC
LIMIT 10;
三大改进:
- 指定强制索引(防优化器抽风)
- 减少返回字段(别用SELECT *!)
- 调整WHERE条件顺序(配合索引最左前缀)
3. 分页优化黑科技
-- 深度分页优化
SELECT * FROM orders
WHERE id > 上一页最后ID
ORDER BY id
LIMIT 10;
当遇到LIMIT 100000,10
这种需求时:
- 改用ID区间查询
- 配合前端滚动加载
- 或者上Elasticsearch!
4. 连接查询的温柔一刀
(原罪SQL)
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LEFT JOIN products p ON o.product_id = p.id
WHERE ...(十多个条件)
优化方案:
- 拆分成多个单表查询
- 使用程序内存关联
- 冗余必要字段到订单表
5. 函数操作的致命陷阱
-- 错误示范
WHERE DATE_FORMAT(create_time, '%Y-%m') = '2023-07'
正确姿势:
WHERE create_time >= '2023-07-01'
AND create_time < '2023-08-01'
6. 终极武器:业务妥协
和产品经理大战三百回合后:
- 取消"实时"查询要求 → 改异步队列
- 砍掉非核心查询条件
- 历史数据归档(3个月前订单移入历史表)
四、效果验证
优化后执行计划:
- 索引范围扫描(type=range)
- Using index condition
- 扫描行数:236行(对比之前230万行!)
- 执行时间:0.18秒 🚀
五、防慢查七大纪律
- EXPLAIN是亲爹:不看执行计划写SQL就是耍流氓
- 索引不是越多越好:每个索引都是UPDATE/DELETE的负担
- 字段精简主义:需要什么查什么,别用SELECT *
- 连接查询节制令:JOIN超过3个表就该报警了
- 分页深水炸弹:OFFSET超过1000就要考虑其他方案
- 定期慢查巡检:每天早上的第一杯咖啡应该配慢查日志
- 架构预防针:热点数据上缓存,复杂查询走搜索中间件
六、血泪经验谈
最近在优化一个统计报表时,发现个反直觉的现象:明明加了索引,查询还是慢。最后发现是字符集不一致导致索引失效!所以千万要注意:
-- 表字段是utf8mb4
ALTER TABLE users MODIFY COLUMN mobile VARCHAR(20)
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 查询使用utf8
SELECT * FROM users WHERE mobile = '13800138000'
COLLATE utf8_unicode_ci; -- 索引失效!
优化永无止境,记住:没有最好的SQL,只有最合适的执行计划。下次遇到慢查询时,深呼吸,打开EXPLAIN,让我们开始愉快的优化之旅吧!