建了索引还是慢?索引失效原因有哪些?这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一算,这索引有啥用?还不如全扫。

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

写在最后

这两个月过得有点魔幻。

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

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

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

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

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

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

下次见。

相关推荐
magic334165635 小时前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
开心-开心急了5 小时前
Flask入门教程——李辉 第一、二章关键知识梳理(更新一次)
后端·python·flask
掘金码甲哥5 小时前
调试grpc的哼哈二将,你值得拥有
后端
小学鸡!6 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
yumgpkpm6 小时前
华为鲲鹏 Aarch64 环境下多 Oracle 、mysql数据库汇聚到Cloudera CDP7.3操作指南
大数据·数据库·mysql·华为·oracle·kafka·cloudera
程序员云帆哥7 小时前
MySQL JDBC Driver URL参数配置规范
数据库·mysql·jdbc
用户21411832636027 小时前
OpenSpec 实战:用规范驱动开发破解 AI 编程协作难题
后端
Olrookie8 小时前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi
LucianaiB8 小时前
招聘可以AI面试,那么我制作了一个AI面试教练不过分吧
后端