数据库索引失效了...

故事背景

数据库有一个大表需要添加几个字段,我要先看有没有其他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 跑飞了才动手,那就真的是亡羊补牢了。

相关推荐
Aska_Lv4 分钟前
RocketMQ---core原理
后端
AronTing9 分钟前
10-Spring Cloud Alibaba 之 Dubbo 深度剖析与实战
后端·面试·架构
没逻辑12 分钟前
⏰ Redis 在支付系统中作为延迟任务队列的实践
redis·后端
雷渊14 分钟前
如何保证数据库和Es的数据一致性?
java·后端·面试
fjkxyl16 分钟前
Spring的启动流程
java·后端·spring
掘金酱16 分钟前
😊 酱酱宝的推荐:做任务赢积分“拿”华为MatePad Air、雷蛇机械键盘、 热门APP会员卡...
前端·后端·trae
脑子慢且灵25 分钟前
MySQL:存储函数和存储过程
数据库·mysql·oracle·存储过程·存储函数
总之就是非常可爱38 分钟前
🚀 使用 ReadableStream 优雅地处理 SSE(Server-Sent Events)
前端·javascript·后端
夜寒花碎42 分钟前
GO入门——Hello, World
后端·go
用户590527632131 小时前
极客时间mysql进阶训练营
mysql