最近面了几家公司,本来觉得自己对 MySQL 索引那几条规则(什么最左匹配、不要在索引列上做运算)背得挺溜,结果前两天被面试官当场上了一课。
面试官问:“你平时怎么判断你的 SQL 到底有没有用到索引?”
我当时自信满满地回答:“只要我按照最左前缀原则写 WHERE 条件,它肯定就走索引了啊。”
面试官听完,盯着我看了三秒钟:“肯定?数据库优化器(Optimizer)是你家开的吗?它走不走索引还得看数据量、回表成本和区分度。你就凭直觉跟我说‘大概’?”
那一刻,我真的汗流浃背了。
回来之后,我对着电脑把EXPLAIN彻底翻烂了。其实判断一个查询是否真的得到了索引,不能靠“我觉得”,得靠数据库给出的“证据”。
1. 唯一证据:EXPLAIN 命令
在 MySQL 里,不管是多么复杂的查询,只要在SELECT前面加上一个EXPLAIN,数据库就会把它的执行计划(Execution Plan)吐出来。
这就好比去医院照 B 超,医生一眼就能看出你肚子里到底是“真有货”还是“全是气”。
EXPLAIN SELECT * FROM user WHERE age = 20;执行完后,你会看到一堆字段。别被那十来个列吓到,只要能看懂这四个核心字段,基本就能稳住面试官。
2. 第一看:key—— 到底最后用了谁?
这是最直接的指标。
possible_keys:代表 MySQL 觉得有哪些索引可以用。这只是“备胎”名单。key:代表 MySQL 真正决定使用的索引。
如果key这一列是NULL,那完了,这查询妥妥地在全表扫描,索引没派上用场。
3. 第二看:type—— 走索引的方式“高级”吗?
这一列是描述查询性能的关键。它显示的是连接类型,从好到坏的常见排序是:
system > const > eq_ref > ref > range > index > ALL
const/eq_ref:这是顶级玩家。通常是你用主键(Primary Key)或者唯一索引(Unique Index)去查询。ref:普通索引命中。range:范围查询。比如WHERE age > 18。虽然用了索引,但要扫一段范围。index:全索引扫描。虽然也是扫索引树,但它是把整个索引树翻一遍,性能只比全表扫描好一丁点。ALL:全表扫描。看到这个,直接原地反思代码。
4. 第三看:key_len—— 索引被“吃”了多少?
这是最容易被忽略、但也最能体现水平的指标。它表示索引使用的字节长度。
如果你建了一个联合索引(a, b, c),通过key_len你能判断出 MySQL 到底是只用了a,还是用了a和b,甚至是a, b, c全用了。
如果你的索引是
int类型且允许为NULL,key_len通常是 5 字节(4 字节数据 + 1 字节标识位)。如果
key_len比你预想的短,说明你的联合索引可能发生了“索引截断”,后面的字段失效了。
5. 第四看:Extra—— 那些隐藏的“潜规则”
这一列经常会出现一些很重要的额外信息:
Using index:这是满分表现!说明发生了“索引覆盖”,你要的数据在索引树里全都有,根本不需要去磁盘回表查整行数据。Using index condition:说明用了“索引下推(ICP)”,MySQL 在扫索引时就把不符合条件的过滤掉了,减少了回表次数。Using filesort/Using temporary:这是警告信号!说明 MySQL 没法利用索引排序,只能在内存甚至磁盘里自己排序,或者建了临时表。看到这个,你的 SQL 性能通常要拉胯。
总结:别凭直觉,看执行计划
以前我写代码总觉得“我写了索引,它就该生效”。但现实是,如果 MySQL 觉得全表扫描比走索引再回表还要快(比如你的WHERE条件过滤掉了 90% 以上的数据),它就会果断抛弃你的索引。
面试官怼得对:在技术面前,直觉是最不靠谱的东西。
下次再有人问你:“你怎么知道索引生效了?”
请冷静地回答他:“我会通过 EXPLAIN 查看 key 是否命中了索引,并结合 type 和 key_len 判断索引利用的广度和深度,最后通过 Extra 确认是否存在回表或索引覆盖。”
这一套词下来,估计面试官端着咖啡的手也要颤抖一下了。