当你的数据库查询从"火箭速度"沦为"龟速爬行",大概率是索引这个"导航系统"罢工了。今天我们就来盘点那些让索引失效的十大"罪魁祸首",看看你踩中了几个?
罪魁祸首1:隐式类型转换——数据库的"鸡同鸭讲"
把字符串类型的索引列当数字用,就像用中文和英文对话——虽然都是人类语言,但就是鸡同鸭讲。比如SELECT * FROM users WHERE user_id = 123,如果user_id是VARCHAR类型,MySQL会默默执行CAST(user_id AS UNSIGNED),这下索引彻底懵了:"我认识的user_id不是这样的啊!"
避坑指南:查询条件的数据类型务必与表结构保持一致,字符串记得加引号:WHERE user_id = '123'。
罪魁祸首2:函数操作索引列——给导航蒙眼开车
在索引列上套函数,相当于给导航仪蒙上眼睛开车。SELECT * FROM orders WHERE YEAR(create_time) = 2023这样的查询,数据库不得不扫描全表计算每个create_time的年份。B+树索引的结构决定了它只能对原始值建立索引,经过函数加工后的值对它来说就是"陌生人"。
优化方案:把函数逻辑转化为范围查询:WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31 23:59:59',让索引能重新"看清"道路。
罪魁祸首3:LIKE前缀 wildcard——索引的"近视眼"
LIKE '%phone'这样的查询会让索引瞬间变成近视眼。B+树索引是按前缀顺序排列的,就像字典只能按首字母查找。当你用%开头时,相当于让字典从中间开始查,索引只能无奈地耸耸肩:"我放弃,全表扫描吧!"
曲线救国:如果业务必须模糊匹配,考虑用全文索引或Elasticsearch,或者把%移到后面:LIKE 'phone%'至少能用上前缀索引。
罪魁祸首4:OR条件中的"猪队友"
OR条件就像团队协作,只要有一个条件没建索引,整个查询就会"团灭"。SELECT * FROM products WHERE price = 100 OR stock > 50中,即使price有索引,只要stock没索引,MySQL就会放弃所有索引走全表扫描——毕竟带着"猪队友"跑步,不如自己慢慢走。
拆分解法:用UNION拆分查询:
SELECT * FROM products WHERE price = 100
UNION ALL
SELECT * FROM products WHERE stock > 50
让每个"精英队员"单独行动,效率反而更高。
罪魁祸首5:联合索引不遵守"最左前缀"——排队插队的后果
联合索引(a,b,c)就像排队买票,必须按顺序来。WHERE b=2 AND c=3相当于跳过a直接找b,索引会生气:"不按规矩排队,我不伺候!"。MySQL只能使用索引的最左前缀列,中间跳过某一列,后面的列都会失效。
排队指南:查询条件必须包含联合索引的最左列,并且不要跳过中间列。实在记不住顺序?记住这句口诀:"带头大哥不能死,中间兄弟不能断"。
罪魁祸首6:范围查询后的索引失效——多米诺骨牌效应
WHERE a=1 AND b>10 AND c=2中,b>10这个范围查询会像推倒多米诺骨牌,让后面的c列索引全部失效。MySQL索引在遇到范围条件后,就无法再使用后续列的索引了——毕竟范围后面的数据顺序已经乱了。
排列技巧:把范围条件放在最后面,让等值条件先过滤大部分数据。或者重新设计索引顺序,把范围查询列放在最后。
罪魁祸首7:不等于操作符(!=、<>)——索引的"叛逆期"
索引天生喜欢确定性,遇到!=就进入叛逆期。SELECT * FROM users WHERE status != 1这样的查询,MySQL优化器会觉得:"反正大部分数据都要查,不如全表扫描算了"。特别是当不满足条件的数据占比很高时,索引反而成了负担。
迂回战术:如果业务允许,用> <代替!=,比如status < 1 OR status > 1,有时能让索引重新工作。
罪魁祸首8:SELECT *——贪婪的代价
SELECT *就像出门旅游把整个家都搬走,不仅增加IO负担,还会让覆盖索引失效。当查询不需要的列时,数据库无法使用仅包含必要列的索引,不得不进行"回表"操作,多跑一趟取数据。
断舍离建议:明确指定需要的列,让索引能"一站式"提供数据。《阿里巴巴开发手册》早就明令禁止使用SELECT *,这不仅是规范,更是性能优化的基本操作。
罪魁祸首9:索引选择性太低——大海捞针的游戏
在gender这样只有"男/女"两个值的列上建索引,就像在人山人海中找戴帽子的人——还不如全表扫描快。当索引列的不同值太少(选择性<10%),MySQL会认为索引没啥用,直接走全表扫描。
价值投资:只为选择性高的列建索引,或者创建包含更多列的复合索引,提高索引的"区分度"。比如(gender, age)的复合索引选择性就比单独的gender索引高得多。
罪魁祸首10:IS NOT NULL——索引的"盲区"
虽然IS NULL通常能使用索引,但IS NOT NULL却是索引的盲区。SELECT * FROM users WHERE email IS NOT NULL这样的查询,由于NULL值在B+树中被特殊处理,MySQL很难高效定位非NULL值,往往选择全表扫描。
数据治理:设计表时尽量避免NULL值,用默认值代替。比如用空字符串''代替NULL,既能使用索引,又能避免NULL值带来的逻辑问题。
如何诊断索引失效?EXPLAIN是你的"CT扫描仪"
遇到慢查询别慌,用EXPLAIN命令给SQL做个CT扫描。重点看这几个指标:
- type:出现ALL表示全表扫描,range是范围扫描,ref/eq_ref才是理想的索引扫描
- key:显示实际使用的索引,NULL表示没用到索引
- Extra:出现Using filesort、Using temporary都是性能警告信号
避坑总结:索引失效的"十宗罪"思维导图
看完这些坑,是不是感觉膝盖中了一箭?其实索引就像个傲娇的导航系统,只要你摸清它的脾气,就能让数据库查询一路畅通。记住:写SQL时多问自己一句"这个查询会让索引失效吗?",就能避免80%的性能问题。