上周帮同事排查生产环境订单系统的卡顿问题,后台慢查询日志里飘着几十条耗时超10秒的SQL,奇怪的是相关字段早就加了索引,怎么还会慢?查了半天才发现,复合索引建反了顺序,再加一个隐式类型转换,直接把索引的作用消耗殆尽,调整完之后查询耗时直接降到0.01秒。这就是索引优化的魔力:用不对的索引不如不加,用对了才能真正把性能拉满。
## 踩过最多的坑:那些自以为是的”正确索引”
很多新手开发者对索引的认知还停留在”给查询字段加索引就会快”,实际上乱加索引不仅不会提升性能,反而会拖慢整个数据库的写入效率——因为每次增删改都要同步维护所有索引,索引越多,写入开销越大。
最常见的错误就是复合索引的顺序搞反,违背了最左前缀匹配原则。举个例子,一张100万条数据的用户表,你经常要执行`select * from user where city=’杭州’ and age=28`的查询,如果把age放在复合索引的第一位,建`index(age,city)`,这个索引的效率会大打折扣。原因很简单:索引的排序要把选择性高的字段放前面,city有几百种不同取值,age只有1-100的区间,把city放前面能快速过滤掉绝大多数不符合条件的数据,反过来的话,索引只能用到age的部分,后续的city条件无法利用索引过滤,本质上还是做了大范围扫描。
还有人喜欢给所有字段都加独立索引,觉得总能用到,实际上MySQL一次查询只能用到一个索引,多个独立索引不仅会让优化器选错索引,还会占用大量磁盘空间,一张核心表的索引最好不要超过5个,过度索引的危害远大于少加一个索引。
## 最容易忽略的隐形杀手:那些会毁掉索引的操作
很多时候你明明建对了索引,查询还是慢,大概率是踩了索引失效的坑,这几个场景是线上问题的重灾区:
第一是隐式类型转换,比如varchar类型的手机号字段,查询时写`where phone=13800001111`,把字符串类型的手机号传成了数字,MySQL会自动把表中所有phone字段转成数字做比较,直接导致全表扫描,索引完全没用。
第二是在索引列上用函数或运算,比如`where year(create_time)=2024`,哪怕create_time加了索引,只要加了函数就会失效,正确的写法是`create_time between ‘2024-01-01’ and ‘2024-12-31’`。
第三是前导模糊查询,`like ‘%北京%’`这种写法不会用到索引,只有后缀模糊`like ‘北京%’`才能利用索引的排序特性。
## 拿来就能用的索引优化落地技巧
说了这么多坑,给大家分享几个能直接落地的优化方法,亲测能解决90%的线上索引问题:
第一是善用EXPLAIN工具分析SQL,执行SQL前加EXPLAIN,重点看type列,如果是ALL就是全表扫描,ref、range才是正常用到索引的状态;再看Extra列,如果出现`Using filesort`或者`Using temporary`,说明你的索引没有支撑排序或分组,需要调整。
第二是用覆盖索引减少回表开销,如果你经常要查`select nickname,phone from user where id=1001`,可以建`index(id,nickname,phone)`的复合索引,查询需要的所有数据都能从索引里拿到,不需要回聚簇索引里查,速度能提升好几倍。
第三是定期清理无效索引,MySQL5.7之后可以通过performance_schema统计索引的使用率,把从来没被调用过的无效索引删掉,既省空间又提升写入性能,很多迭代了好几年的老项目,都能清出一半以上的无用索引。
## 总结与行动号召
索引不是提升性能的万能药,却是MySQL性能优化的核心抓手,核心原则记住:索引不在多而在精,建对顺序、避开失效坑、定期维护,才能让索引真正发挥作用。
今晚抽10分钟,登录你负责的测试环境数据库,拉3条最近的慢查询,用EXPLAIN跑一遍,看看你的索引是不是真的生效了,再清掉两三个没人用的旧索引,你的数据库性能就能悄悄往上跳一个档。






