在数据库优化时,我们常听到“加个索引就能提高查询速度”。但现实中,很多人加了索引后,查询依然很慢。这就像给汽车装了个涡轮增压,但还是跑不快,可能是别的地方出了问题。
今天我们就来看看 MySQL 索引优化的几个常见坑,看看你有没有踩过!
就像书籍分类号格式不匹配
问题
如果查询条件的字段类型和索引类型不一致,MySQL 可能会进行隐式转换,导致索引失效。
案例
SELECT * FROM users WHERE phone = 13800138000;
假设 phone 是 VARCHAR 类型,而查询时写了一个数字(INT),MySQL 会把 phone 这一列的所有值都转换成数字再比较,索引就失效了。
解决方案
保持数据类型一致,比如改成:
SELECT * FROM users WHERE phone = '13800138000';
避坑指南:
就像图书馆查找书籍时跳过了分类号的开头
问题
当 查询条件中对索引列做了运算、函数或类型转换,MySQL会直接放弃索引,触发全表扫描。
案例
select * from user where id + 1 = 2 ;
SELECT * FROM user WHERE YEAR(create_time) = 2023;
即使 id /create_time 有索引,会先进行全表扫描然后对索引字段进行计算处理,导致无法正常走索引。
解决方案
使用预设好的值
select * from user where id = 3;
SELECT * FROM user WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
就像你让图书管理员(MySQL)找“所有不是科幻类”的书,管理员看了看目录(索引),发现科幻类只占1%,剩下的99%都要翻——于是干脆扔掉目录,直接全馆扫描(全表扫)更快!
问题:
对索引列使用 !=、<>、IS NOT NULL、NOT IN、NOT EXISTS 等否定条件时,MySQL可能放弃索引。因为否定条件往往需要扫描大量数据,优化器认为全表扫描更划算。
案例:
SELECT * FROM order WHERE status != 1; -- status字段有索引但用不上
SELECT * FROM user WHERE phone IS NOT NULL; -- 如果phone字段大部分非NULL,索引失效
解决方案
– 优化思路:拆分条件或使用覆盖索引
-- 示例1:明确查询范围(假设status只有0/1/2)
SELECT * FROM order WHERE status IN (0, 2);
-- 示例2:覆盖索引减少回表
SELECT id FROM user WHERE phone IS NOT NULL; -- 仅查询索引字段
避坑指南:
-慎用否定查询:尽量用正向条件(如IN、=)替代 NOT IN、!=。
数据分布决定行为:如果否定条件筛选的数据极少(比如status != 1且status=1占99%),可以强制走索引(但需实测验证性能)。
覆盖索引补救:若必须用否定条件,尝试让查询字段完全被索引覆盖,减少回表代价。
最左匹配原则:以最左边字段的为起点查询可以使用联合索引
问题
创建了组合索引 (a, b, c),但查询时没按照最左前缀法则使用,导致索引无法生效。
案例
SELECT * FROM orders WHERE b = 2 AND c = 3;
如果 a 没有在查询条件里,索引就无法被正常利用。
解决方案
使用索引的最左前缀法则,例如:
SELECT * FROM orders WHERE a = 1 AND b = 2 AND c = 3;
避坑指南:
就像从书本的最后一页开始翻
问题
当 LIKE 查询的模式以 % 开头,索引无法使用,因为 MySQL 需要扫描所有数据。
案例
SELECT * FROM products WHERE name LIKE '%手机';
解决方案
尽量避免 % 开头,可以使用前缀匹配:
SELECT * FROM products WHERE name LIKE 'iPhone%';
就像在图书馆用两种方式查找书籍,但只有一种方式有目录
问题
如果 OR 两边的字段没有同时命中索引,MySQL 可能会放弃索引,进行全表扫描。
案例
SELECT * FROM customers WHERE name = '张三' OR phone = '13800138000';
如果 name 有索引,但 phone 没有,可能导致全表扫描。
解决方案
确保 OR 两边的字段都有索引,或者改用 UNION 来优化:
SELECT * FROM customers WHERE name = '张三'
UNION
SELECT * FROM customers WHERE phone = '13800138000';
像查字典还要翻页
问题
查字典时,目录(索引)只告诉你目标字的页码(主键ID),你还要翻到那页(回表)才能看到详细解释。
如果查询字段未完全被索引覆盖,即使走了索引,仍需回表查完整数据。
案例
SELECT * FROM product WHERE category = '手机';
解决方案
覆盖索引(仅查询索引包含的字段)
SELECT id, name FROM product WHERE category = '手机';
避坑指南:
总结
MySQL 优化不只是加索引,还需要正确使用索引。希望这些坑你能避开,让数据库跑得飞快!