【大白话说Java面试题 第78题】【Mysql篇】第8题:解释下最左前缀原则?
第8题解释下最左前缀原则回答核心考点大厂面试要求不仅知道“查询必须从最左列开始”更要深入理解底层B树排序原理、范围查询导致后续列失效的机制、跳过列时索引下推的工作方式以及如何设计联合索引顺序。面试官常追问“最左前缀一定需要最左列吗为什么”1. 最左前缀原则的定义最左前缀原则是指对于联合索引MySQL会从索引的最左列开始匹配查询条件只有满足最左前缀的查询才能利用该索引加速检索。联合索引的本质联合索引(a, b, c)相当于创建了三个索引一个按a排序的索引一个按(a, b)排序的索引一个按(a, b, c)排序的索引因此(a, b, c)这个联合索引可以用于匹配(a)、(a, b)、(a, b, c)的查询条件但不能用于匹配(b)或(c)的查询。2. 联合索引的B树排序原理深入理解为什么必须从最左列开始把联合索引想象成一个电话本电话本先按姓氏排序姓氏相同再按名字排序要查找“所有叫小明的人”只给名字你无法直接翻到那一页因为电话本不是按名字排的必须先定位到姓氏才能在姓氏内部找到名字联合索引(a, b, c)的B树排序规则先按a排序a相同时按b排序a和b都相同时按c排序这就是为什么查询条件必须包含最左列——没有最左列a索引的B树就无法定位起始搜索位置只能全索引扫描甚至全表扫描。3. 命中规则速查表以联合索引(a, b, c)为例WHERE条件是否命中使用哪些列说明WHERE a 1✅ 完全命中a匹配最左列WHERE a 1 AND b 2✅ 完全命中a, b匹配最左两列WHERE a 1 AND b 2 AND c 3✅ 完全命中a, b, c匹配所有列WHERE a 1 AND c 3⚠️ 部分命中ac用ICP跳过bc无法用于索引范围查找WHERE b 2❌ 不命中无未从最左列开始WHERE c 3❌ 不命中无未从最左列开始WHERE b 2 AND c 3❌ 不命中无未从最左列开始特殊情况如果查询条件是WHERE b 2 AND a 1MySQL优化器会自动重排为WHERE a 1 AND b 2能够命中索引。这是因为MySQL的查询优化器会自动调整AND条件顺序以匹配索引的最左前缀。4. 跳过中间列的场景(a, c)能走索引吗结论能部分命中但c列无法用于索引范围查找。原理索引按(a, b, c)顺序排序数据先按a排a相同再按b排b相同再按c排WHERE a1 AND c3时MySQL用a1定位到索引中的一段范围但由于b没有出现在条件中无法精确定位到某条记录索引在b这一层就断了c列在b无序的情况下也处于无序状态无法用B树二分查找直接定位到c3的位置那c的条件还能用吗——索引下推ICPMySQL 5.6引入索引下推Index Condition Pushdown, ICP在索引扫描过程中对索引中包含的字段先做判断提前过滤掉不满足条件的记录对于WHERE a1 AND c3会在索引层用c3过滤掉部分数据减少回表次数但c仍然无法用于索引范围查找即无法像a那样用二分查找快速定位5. 范围查询为什么后面的列索引会失效结论范围查询、、BETWEEN、LIKE xxx%之后的索引列全部失效。原因索引是按顺序排列的。等值查询能精确定位到某个节点后续列可以继续在该节点下排序查找但范围查询返回的是一段区间这段区间内后续列的值是无序的例如WHERE a1 AND b20 AND c3在a1且b20的区间内c的值不是有序的无法用索引快速定位c3关键区分等值查询、IN可优化器重排后续列可以继续用索引范围查询、、BETWEEN、LIKE xxx%后面的列索引失效6. 设计联合索引顺序的黄金法则铁律1等值查询列放前面范围查询列放后面-- 查询条件WHEREuser_id123ANDorder_date2026-01-01-- 正确设计索引(user_id, order_date)-- user_id等值精确定位 → order_date范围查找CREATEINDEXidx_user_dateONorders(user_id,order_date);-- 错误设计索引(order_date, user_id)-- order_date范围后user_id无法用索引铁律2高区分度列放前面-- 错误status只有3个值区分度极低放最左CREATEINDEXidx_wrong(status,user_id);-- 正确user_id有1万个不同值区分度高放最左CREATEINDEXidx_right(user_id,status);对比100万行数据status2匹配约33万行全表33%user_id10086匹配约100行0.01%铁律3如果有ORDER BY把排序列放最后-- 查询WHEREuser_id123ORDERBYcreate_timeDESC-- 索引(user_id, create_time)-- 既能快速过滤user_id又能让create_time天然有序避免filesort铁律4通过调整顺序减少索引数量由于支持最左前缀有了(a, b)索引后一般不需要再单独建(a)索引。7. 常见错误 vs 正确做法错误写法为什么错正确做法WHERE a 1 AND b 2建索引(a, b)范围a放左边b等值失效建索引(b, a)等值放前WHERE a 1 AND c 3建索引(a, b, c)跳过bc无法索引查找如b无条件可建(a, c)或接受ICPWHERE a 1 AND b 2 AND c 3建索引(a, b, c)b范围后c失效建索引(a, c, b)把c放b前ORDER BY b索引是(a, b)且a无条件不满足最左前缀ORDER BY无法用索引建索引(b)或给查询加a条件低基数列放最左如status区分度极低扫描大量数据高区分度列放最左8. 面试官追问与高分回答Q1最左前缀原则的底层原理是什么A联合索引在B树中按从左到右的顺序排序存储。先按第一列排序第一列相同再按第二列排序依此类推。这种存储结构决定了只有从最左列开始的连续列才能利用索引的有序性进行二分查找。跳过最左列MySQL无法定位起始位置。Q2WHERE a 1 AND c 3能走索引(a, b, c)吗c的条件有用吗A能部分命中——a可以用于索引查找。c的条件在MySQL 5.6中通过**索引下推ICP**在索引层提前过滤减少回表次数。但c无法用于索引范围查找即不能用B树的二分查找直接定位c3的位置因为跳过了bc在a1的范围内是无序的。Q3为什么范围查询后面的列索引会失效A等值查询能精确定位到B树中的某个节点后续列在该节点下仍然有序。但范围查询返回的是一个区间这个区间内后续列的值不再有序无法用索引快速定位。Q4联合索引(a, b, c)WHERE a 1 AND b IN (2,3) AND c 4能用到所有列吗AIN条件在优化器处理中等同于多个等值不属于范围查询。如果b的值数量可控索引可以用完(a, b, c)三列。Q5设计联合索引时顺序怎么排A三大原则①等值查询列放前面范围查询列放后面②高区分度列放前面③有ORDER BY时排序列放最后。还要考虑索引复用——已有(a, b)可覆盖(a)查询。9. 实战案例优化慢查询场景订单表几百万行查询用户123的“已完成”订单按订单日期倒序只要前20条。-- 原SQLSELECT*FROMordersWHEREuser_id123ANDstatus已完成ORDERBYorder_dateDESCLIMIT20;-- 原索引只有(user_id)-- 问题过滤user_id后还有5000条需要逐一过滤status再排序优化过程考虑因素设计决策等值条件user_id、status都是等值 → 放前面排序列order_date→ 放最后希望不回表如只查(user_id, status, order_date)可用覆盖索引-- 最终索引ALTERTABLEordersADDINDEXidx_uid_status_date(user_id,status,order_date);效果扫描行数从5000降至86无filesort速度从几百毫秒降到几毫秒。10. 总结对比表查询条件类型索引(a, b, c)的使用情况优化手段(a)✅ 完全命中a—(a, b)✅ 完全命中a, b—(a, b, c)✅ 完全命中三列—(a, c)⚠️ 仅命中ac用ICP如b确实无查询条件可建(a, c)(b)❌ 完全不命中为b单独建索引范围后列❌ 范围后的列索引失效调整顺序范围列放最后低基数列⚠️ 能命中但效率差高区分度列放最左面试官想要的满分总结“最左前缀原则的本质是B树的排序特性——联合索引(a, b, c)按照a、然后b、然后c的顺序排序。查询时必须从最左列开始才能利用B树的有序性进行二分查找定位。命中规则(a)、(a, b)、(a, b, c)能完全命中(a, c)只能命中ac可通过MySQL 5.6的索引下推在索引层过滤减少回表(b)完全不命中。范围查询陷阱等值查询列放前面范围查询列放后面。因为范围查询会破坏后续列的有序性导致索引失效。设计原则①等值前列范围后排②高区分度列放左③有ORDER BY时排序列放最后④利用最左前缀特性通过调整顺序减少冗余索引。一句话最左前缀原则是联合索引的生命线——用对了一飞冲天用错了等于白建。”PDF大白话说Java面试题 — 03-Mysql篇