数据库索引失效了...

故事背景

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

相关推荐
CHENWENFEIc30 分钟前
SpringBoot论坛系统安全测试实战报告
spring boot·后端·程序人生·spring·系统安全·安全测试
叁沐37 分钟前
MySQL 04 深入浅出索引(上)
mysql
q9085447031 小时前
MySQL 二进制日志binlog解析
mysql·binlog·binlog2sql·my2sql
重庆小透明1 小时前
力扣刷题记录【1】146.LRU缓存
java·后端·学习·算法·leetcode·缓存
博观而约取1 小时前
Django 数据迁移全解析:makemigrations & migrate 常见错误与解决方案
后端·python·django
码不停蹄的玄黓2 小时前
MySQL分布式ID冲突详解:场景、原因与解决方案
数据库·分布式·mysql·id冲突
寻月隐君2 小时前
Rust 异步编程实践:从 Tokio 基础到阻塞任务处理模式
后端·rust·github
GO兔2 小时前
开篇:GORM入门——Go语言的ORM王者
开发语言·后端·golang·go
Sincerelyplz2 小时前
【Temproal】快速了解Temproal的核心概念以及使用
笔记·后端·开源
爱上语文2 小时前
Redis基础(6):SpringDataRedis
数据库·redis·后端