建了索引还是慢?索引失效原因有哪些?这10个坑你踩了几个

前言

好久不见。

断更挺长时间了,说出来都不好意思。

这两个月啊,真的是。团队走了几个老人,又来了一堆新人。业务方向也一直在变,今天说往东,明天又往西了。每天忙成狗,晚上回家躺床上就睡着了,连刷手机的力气都没有。

最近才算缓过来点。

昨天面试了个小伙子,我问他索引啥时候会失效,他想了半天,说了个LIKE的情况。我说还有呢?他就卡住了。其实他不是不知道,就是没系统地想过这个问题。

后来我翻了翻自己的笔记,想想不如整理一下发出来。也不求多深入,就是把常见的坑列一列。你要是准备面试,看完这篇,至少能答得有条理点。

至于为什么会这样,背后的原理啥的,咱们后面再聊。

一、对索引列用函数或做计算

先说第一个,我自己就踩过。

去年有个需求,要查2024年注册的用户。我当时偷懒,直接写了个 YEAR(create_time) = 2024。看着没问题吧?结果查了半天,转圈圈转了快一分钟。

我还纳闷呢,create_time 明明建了索引的啊。

后来 EXPLAIN 看了一眼,好家伙,全表扫描。

看个对比你就懂了:

sql 复制代码
-- ❌ 这样写,索引废了
SELECT * FROM users WHERE YEAR(create_time) = 2024;
SELECT * FROM orders WHERE amount + 10 > 1000;

-- ✅ 得这么写
SELECT * FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';
SELECT * FROM orders WHERE amount > 990;

为啥?你想啊,你对索引列做函数处理,数据库怎么用索引?它得把每条数据拿出来,先算个YEAR(),再跟2024比。那不就是挨个遍历嘛。

所以记住,别动索引列。

要算,你在右边算。

二、类型没对上

这个更坑,不太容易发现。

大概的意思可以看这个:

sql 复制代码
-- phone 是 VARCHAR 类型
-- ❌ 完蛋,没加引号
SELECT * FROM users WHERE id = 13800138000;

-- ✅ 得加引号
SELECT * FROM users WHERE phone = '13800138000';

就差一对引号。

MySQL看到你拿数字去跟字符串比,它会自动转换。把phone转成数字再比。转换就相当于对phone做了函数操作,跟上面说的一样。

当然这个例子比较牵强,意会即可。

不管怎么说,对待类型得细心。

三、LIKE 前面带了百分号

这个知道的人很多,但还是有人会踩坑。

做搜索时,产品说要支持模糊搜索,搜啥都能搜出来。开发一听,那简单,LIKE '%关键词%' 走起。

结果呢?数据量大起来后就卡爆了。

sql 复制代码
-- ❌ 这样写,索引白建
SELECT * FROM products WHERE name LIKE '%手机%';
SELECT * FROM products WHERE name LIKE '%iPhone';

-- ✅ 只有这样才能走索引
SELECT * FROM products WHERE name LIKE 'iPhone%';

你想啊,索引就跟字典一样,按顺序排的。你要找"iPhone开头"的,好办,直接翻到I那页。但你要找"包含iPhone"的,那它怎么办?只能从A翻到Z,每个都看一遍。

这样是不是就很好理解了。

所以遇到这种需求,跟产品商量商量,能不能只支持前缀搜。

实在不行,考虑上ES吧。

四、用了 OR

OR 看着挺正常的,实际上有坑。

有一回写查询,要找名字叫张三的,或者年龄是25的。我就很自然地写了个OR。跑起来慢得要死。

sql 复制代码
-- 假设 name 有索引,age 没有
-- ❌ 全表扫描了
SELECT * FROM users WHERE name = '张三' OR age = 25;

-- ✅ 两个字段都得有索引
SELECT * FROM users WHERE name = '张三' OR email = 'test@example.com';

问题在哪呢?name有索引,age没有。MySQL一看,OR两边都得查,一个能走索引,一个不能。它想了想,算了,干脆全表扫吧,省事。

所以用OR得小心点。

五、联合索引没从左边开始

面试爱问这个。

假设你建了个联合索引,(name, age, city)。看下面这几个查询:

sql 复制代码
-- 假设有联合索引 (name, age, city)

-- ✅ 这些可以
SELECT * FROM users WHERE name = '张三';
SELECT * FROM users WHERE name = '张三' AND age = 25;

-- ❌ 这些不行
SELECT * FROM users WHERE age = 25;
SELECT * FROM users WHERE city = '北京';

为啥?

联合索引得从最左边那个字段开始用,不能跳。还是上面查字典的例子,或者就跟图书馆找书一样,你得先找大分类,再找小分类,最后找具体的书。

肯定不能直接跑去找书名吧?那不还得把整个馆翻一遍。

面试被问到联合索引,十有八九会问这个。记住就行。

六、用了 NOT、!= 这些

否定条件,基本走不了索引。

sql 复制代码
-- ❌ 没戏
SELECT * FROM users WHERE status != 1;
SELECT * FROM users WHERE age NOT IN (18, 19, 20);

你想啊,"不是1",那是2?3?4?还是其他的?范围太大了。MySQL算了算,还不如直接全表扫。

能的话,改成正向的。比如status就3个值,那我直接查 IN (2, 3) 不就得了。

七、IS NULL 这种

这个得看情况。

sql 复制代码
-- ❌ 不一定走索引
SELECT * FROM users WHERE email IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;

表里大部分email都是空的,你查IS NULL,那不是要扫一大片?MySQL一看,算了,全表扫吧。

反过来也一样。

所以建表的时候,尽量别让字段为空。给个默认值,省事。

八、SELECT *

这个不算失效,但影响性能。

我刚工作那会儿,老大看我写SQL,上来就说:别用星号。

我当时还不服气,用星号多方便啊。后来才懂:

sql 复制代码
-- ❌ 得回表查所有字段
SELECT * FROM users WHERE name = '张三';

-- ✅ 只查需要的,快
SELECT id, name FROM users WHERE name = '张三';

你查的字段都在索引里,数据库直接从索引拿给你,不用回表。这叫覆盖索引。

但你一用星号,它得回表把所有字段都查出来。多一次IO,数据量大了,差距就明显了。

所以,用啥查啥。别偷懒。

九、数据太少

有时候挺奇怪的。测试环境跑得好好的,走索引。一到生产环境就不走了。

可能不是代码问题,是数据量。

表里就几千条数据,MySQL想了想,全扫一遍也就几毫秒,还折腾啥索引树啊。直接不用了。

这个没办法,等数据量上去就好了。所以测性能,得用真实的数据量。不然测了也白测。

十、索引重复率太高

还有一种。

比如性别字段,就俩值,男和女。你建了索引,查"性别=男",一半数据都是男的。MySQL一算,这索引有啥用?还不如全扫。

所以建索引前,看看重复率。太高的话,建了也没用。

写在最后

这两个月过得有点魔幻。

团队走了一拨人,来了一拨人。业务方向改来改去,今天这么干,明天那么干。每天忙得跟陀螺似的,回头一看,好像啥也没干成。

那感觉吧,就跟索引失效了一样。明明在跑,就是不出活。

现在好点了,坐下来整理整理,也算给自己个交代。

其实写代码跟做人差不多。建了索引,不一定能跑得快。方向不对,优化器再聪明也没用。人生可能也一样,选错路了,再怎么努力都白搭。

希望咱们都能找到自己的最左前缀吧。

好了,不早了,今天就这样。

下次见。

相关推荐
小白银子1 小时前
零基础从头教学Linux(Day 62)
数据库·mysql·oracle
Boilermaker19924 小时前
【MySQL 进阶】高性能优化
数据库·sql·mysql
Yeats_Liao4 小时前
时序数据库系列(五):InfluxDB聚合函数与数据分析
java·后端·数据分析·时序数据库
CoderOnly5 小时前
SQL,CROSS JOIN速度优化
数据库·sql·mysql
老衲提灯找美女6 小时前
MySQL的增删改查功能合集
数据库·mysql·增删改查·增删改查详细用法
你的人类朋友7 小时前
✍️记录自己的git分支管理实践
前端·git·后端
像风一样自由20207 小时前
Go语言入门指南-从零开始的奇妙之旅
开发语言·后端·golang
白鹿第一帅7 小时前
【仓颉纪元】仓颉性能优化深度实战:5 天让应用提速 300%
性能优化·内存管理·性能分析·编译优化·仓颉语言·并发优化·ui渲染优化
合作小小程序员小小店7 小时前
web网页开发,在线考勤管理系统,基于Idea,html,css,vue,java,springboot,mysql
java·前端·vue.js·后端·intellij-idea·springboot
小马爱打代码8 小时前
MyBatis:性能优化实战 - 从 SQL 优化到索引设计
sql·性能优化·mybatis