文章目录
最近在技术社区看到一个有趣的投票:程序员职业生涯中最怕遇到的十大问题中,慢查询优化竟然高居前三(仅次于线上故障和生产数据丢失)!今天咱们就来场硬核实战,把我去年处理的一个真实慢查询案例拆解给你看,绝对比教科书上的示例更接地气!
一、问题定位:慢查询的"犯罪现场"
当时接手的是一个电商平台的订单统计报表功能,用户反馈导出的Excel经常卡死。通过MySQL的慢查询日志(show variables like ‘slow_query%’),我们很快锁定了这个耗时5.2秒的"元凶":
SELECT
o.order_id,
u.username,
p.product_name,
SUM(oi.quantity) AS total_quantity,
COUNT(DISTINCT oi.sku_id) AS sku_variants
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.create_time BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY o.order_id
HAVING sku_variants > 3
ORDER BY total_quantity DESC
LIMIT 1000;
EXPLAIN诊断报告(关键指标):
- type: ALL(全表扫描警告!)
- rows: 280万(触目惊心)
- Extra: Using temporary; Using filesort(双重暴击)
二、优化五步走战略
第一步:索引手术刀(关键!)
-- 原有索引
ALTER TABLE orders ADD INDEX idx_user (user_id);
-- 优化后复合索引
ALTER TABLE orders ADD INDEX idx_time_user (create_time, user_id);
ALTER TABLE order_items ADD INDEX idx_order_product (order_id, product_id);
原理:把WHERE条件字段和JOIN字段组合成复合索引,利用最左前缀原则。实测查询速度从5.2s→1.8s!
第二步:拆解复杂查询
把HAVING条件转移到子查询:
SELECT * FROM (
SELECT
... -- 原查询字段
GROUP BY o.order_id
) AS temp
WHERE sku_variants > 3
效果:减少GROUP BY后的数据量,执行时间1.8s→1.2s
第三步:拒绝临时表
在my.cnf增加配置:
tmp_table_size=256M
max_heap_table_size=256M
注意:服务器内存小于32G的不要盲目调大!我们案例中临时表大小约180M,调整后Using temporary消失,耗时1.2s→0.9s
第四步:文件排序优化
-- 原ORDER BY
ORDER BY total_quantity DESC
-- 优化为预计算
ALTER TABLE orders ADD COLUMN total_quantity_cache INT;
UPDATE orders SET total_quantity_cache = (...) -- 定期更新
取舍:用空间换时间,适合统计类查询。耗时0.9s→0.4s
第五步:终极武器——异步处理
对于前端导出Excel的需求,改为:
- 用户点击导出后立即返回
- 后台跑任务生成文件
- 完成后站内信通知
体验提升:用户感知延迟从5秒→0.5秒!
三、避坑指南(血泪教训)
- 索引不是银弹:遇到过添加索引后写入速度下降70%的案例,特别是UUID主键的表
- JOIN的黑暗面:超过3表关联时,试试拆分成多个简单查询
- 分页陷阱:
LIMIT 1000,10
比WHERE id>1000 LIMIT 10
慢10倍不止! - 隐式转换:
WHERE user_id = '123'
(字段是INT)会导致索引失效
四、性能监控三板斧
- Percona Toolkit:
pt-query-digest
分析慢日志 - Prometheus+Grafana:实时监控QPS、连接数等指标
- 压力测试:用sysbench模拟真实负载
五、彩蛋:面试高频题解析
面试官:“你说说索引失效的常见场景?”
参考答案:
- 最左前缀缺失(比如索引是(a,b),只查b)
- 对索引列做运算(WHERE YEAR(create_time)=2023)
- 使用LIKE以通配符开头(LIKE ‘%abc’)
- 不同类型比较(字符串字段用数字查询)
- OR条件未全覆盖(改进方案:用UNION替代)
最后说点掏心窝的
优化就像治病,最忌讳"头痛医头脚痛医脚"。去年我们团队做过一个统计:70%的慢查询问题通过调整业务逻辑解决,而不是纯技术优化。比如把实时统计改为定时任务,或者在前端增加过滤条件。
记住:最好的优化,是让查询不必执行! (这句话值十个鸡腿🍗)
下期预告:《EXPLAIN结果全字段解析:从入门到入土》 👨💻
点赞过百马上开肝!有什么想看的优化场景欢迎评论区点菜~