故事背景
数据库有一个大表需要添加几个字段,我要先看有没有其他sql对这个表做操作。
SELECT * FROM information_schema.processlist WHERE command != 'Sleep';
然后发现有一个更新的SQL语句跑了几个小时,用explain语句发现对这个大表做了全表扫描...
sql
explain update order_info oi
INNER JOIN pay_plat pp on oi.mch_order = pp.merchant_order_id
set oi.product_name = pp.product_name,oi.merchant_data_package = pp.merchant_data_package
where oi.create_time between '2024-09-09' and '2024-09-10'
问题排查
首先我是觉得可能是SQL写错了,但是换了几个SQL的写法都好像不行,就很神奇。
1、不用inner join,直接关联(其实是一样的)
sql
explain
select oi.mch_order,pp.product_name,pp.merchant_data_package
from order_info oi,pay_plat pp
where oi.create_time between '2024-09-09' and '2024-09-10'
and oi.mch_order = pp.merchant_order_id
2、使用子查询
sql
explain update ( select * from order_info where create_time between '2024-09-09' and '2024-09-10') oi
INNER JOIN pay_plat pp on oi.mch_order = pp.merchant_order_id
set oi.product_name = pp.product_name,oi.merchant_data_package = pp.merchant_data_package
这样写好像也是不行的,子查询不能当做update表 报错:The target table order_info of the UPDATE is not updatable
问了一下gpt,他给我几个优化建议:
1、将日期范围查询改为精确查询(不符合需求)
2、使用强制索引(不行)
3、确保索引覆盖查询(已经确认过字段上都有索引)
4、使用子查询(试过不行)
后面看了一下两个表的编码格式不一样,一个是utf8,一个是utf8mb4,难道就是这个原因?后面改了一下表结构,都改成mb4,explain一下还是不行。然后看了下字段还是utf8,把字段改成utf8mb4要执行很长时间,现在7000万数据执行了半个多小时都还没执行完毕......执行完之后再explain一下看看行不行,估计就是这个问题了。查询这个DDL语句,copy to tmp table这个状态是搞了个临时表。
字段修改完成之后,再查一下发现是走了索引了,大家一定要注意字段编码类型的问题~
索引失效场景
最后再复习一下索引失效场景:
1、like模糊查询
如果使用 LIKE
且通配符 %
在查询条件的开头,索引将失效。最近发现时间用like查一天的天数也会不走索引,比如create_time like '2024-09-10%'。用create_time between '2024-09-10' and '2024-09-11' 这样就可以走索引。
2、索引列有函数或者有计算
3、联合索引最左定则
4、索引列类型不一样,比如id = '字符串',或者编码格式不一样?
5、使用or
6、字段隐式转换
7、使用 !=,not in
等等
最简单还是用explain语句查看sql语句是否走索引,不走的话再一一排查。
总结
大表查询时一定要注意索引是否真正生效,别被"数据量小的时候还能跑得动"给骗了。很多 SQL 在数据量小的时候看不出问题,但一旦数据上来了,没有合适的索引或者写法不合理,性能就会成倍、甚至成指数级地下滑。
我这边遇到的情况是这样的:一年前写的一个 SQL 查询,当时跑一次大概需要 4 分钟,虽然有点慢,但还能接受。结果最近再跑同一个 SQL,发现居然要跑整整 4 个小时!一查才发现,原来某个 join 没有走索引,加上表又变大了,造成了巨大的笛卡尔积,中间还拼命扫全表。
后来花时间重新分析执行计划,调整了索引、改了连接方式,优化之后再跑这条 SQL------只用了 7 秒!从 4 小时到 7 秒,性能直接提升了近 2000 倍!
总结几点教训:
- 联表查询务必确认 join 条件合理,防止隐式笛卡尔积;
- 索引不是建了就一定能用上,要结合执行计划看是否生效;
- 不要等系统变慢了才去优化,SQL 写好之后就应该考虑未来扩展;
- 定期 review 关键 SQL,尤其是跑在大表、大数据量上的。
性能优化真的是"早改早轻松",等 SQL 跑飞了才动手,那就真的是亡羊补牢了。