面试时被问到 “MySQL 建索引要遵循哪些原则”,你是不是只会说 “主键要建索引”“经常查询的字段建索引”?这种泛泛而谈的回答,连面试官的基础期待都满足不了。
真正能打动面试官的回答,一定是 “带着业务场景、带着坑点、带着优化逻辑” 的 —— 比如 “我们做订单系统时,给‘用户 ID + 订单创建时间’建了联合索引,因为业务里高频查询是‘查某个用户近 30 天的订单’,单字段索引要么查得慢,要么覆盖不了查询字段;但一开始没注意顺序,把‘订单创建时间’放前面,导致查询时索引失效,后来调整顺序才解决问题”。
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份面试小册:Java面试题,简历模板、学习路线,需要的小伙伴→ 这里
MySQL 索引的本质是 “帮数据库快速定位数据的‘目录’”,但建索引不是 “越多越好”,也不是 “想建就建”。以下 7 个原则,是从无数次业务实战中总结的 “避坑指南”,更是面试时能体现你实战能力的关键考点。
一、核心原则:“高频查询字段优先建,低频查询字段别乱建”
索引的核心价值是 “加速查询”,如果一个字段半年才查一次,建索引不仅没用,还会拖慢插入、更新、删除的速度(因为每次数据变动,索引都要同步更新)。
- 优先给 “WHERE 条件里的高频字段” 建索引
比如用户表(user)里,“手机号(phone)” 是登录时的高频查询字段,必须建索引;而 “用户备注(remark)” 字段,只有客服偶尔查询,就没必要建索引。
- 别给 “区分度低的字段” 建索引
区分度是指 “字段不同值的比例”,比如 “性别(gender)” 字段只有 “男 / 女 / 未知”3 个值,区分度极低,建索引毫无意义 —— 因为即使通过索引定位,也会匹配到大量数据,最后还是要全表扫描。 二、联合索引:“最左前缀匹配是铁律,字段顺序别搞反”
业务里很少只用单个字段查询,比如订单表高频查询是 “查用户 ID 为 123、且创建时间在 2024 年 1 月后的订单”(WHERE user_id=123 AND create_time>'2024-01-01'),这时需要建 “user_id+create_time” 的联合索引 —— 但联合索引的字段顺序,直接决定索引是否生效,这也是面试高频坑点。
- 遵循 “最左前缀匹配原则”:过滤条件放前面
MySQL 联合索引的查询,会从左到右依次匹配字段,只要左边的字段没匹配到,后面的字段就不会生效。
比如建了 “user_id+create_time” 的联合索引:
- 当查询条件是 “user_id=123 AND create_time>'2024-01-01'” 时,两个字段都能用到索引,查询很快;
- 但如果查询条件是 “create_time>'2024-01-01'”(没带 user_id),索引就会失效,变成全表扫描;
- 甚至查询条件是 “create_time>'2024-01-01' AND user_id=123”(字段顺序反过来),MySQL 优化器会自动调整顺序,还是能用到索引 —— 但这不代表顺序可以乱建,因为还有 “覆盖索引” 的需求。
- 把 “过滤性强的字段放前面”
过滤性强,指的是 “这个字段能快速缩小查询范围”。比如订单表中,“user_id” 能定位到某个用户的几十条订单,而 “create_time” 只能定位到某段时间的几万条订单,所以 “user_id” 的过滤性更强,要放在联合索引的前面。
- 尽量用 “联合索引覆盖查询”,避免回表
如果查询的所有字段,都在联合索引里,MySQL 就不用 “先查索引,再回表查数据”(回表会浪费时间),这就是 “覆盖索引”。
比如查询 “user_id=123 AND create_time>'2024-01-01'” 时,需要返回 “订单号(order_no)、金额(amount)”,如果联合索引建为 “user_id+create_time+order_no+amount”,那么查询时直接从索引里拿数据,不用回表,速度会快很多。
三、警惕 “索引失效”:这些场景别踩坑
很多时候,你以为建了索引,但查询时却没用到,原因就是触发了 “索引失效”。这些场景,既是业务里的常见坑,也是面试必问的考点。
- 别在索引字段上做 “函数操作”
比如给 “create_time” 建了索引,但查询时写 “WHERE DATE (create_time) = '2024-01-01'”,MySQL 会对每一行的 “create_time” 做函数计算,无法使用索引,只能全表扫描。
正确做法:改成 “WHERE create_time BETWEEN '2024-01-01 00:00:00' AND '2024-01-01 23:59:59'”,直接匹配索引字段,索引生效。
- 避免 “字段类型不匹配”
比如 “phone” 字段是 VARCHAR 类型,但查询时写 “WHERE phone=13800138000”(用数字匹配字符串),MySQL 会做类型转换,导致索引失效。
实战教训:之前做用户登录功能时,就踩过这个坑 —— 手机号存的是 VARCHAR,查询时传了数字,导致索引失效,登录接口响应时间从 50ms 变成了 800ms,后来把查询条件改成 “phone='13800138000'” 才解决。
- 别用 “!=、<>、NOT IN” 等否定条件
否定条件会让 MySQL 认为 “需要匹配的数据很多”,从而放弃索引,选择全表扫描。比如 “WHERE user_id != 123”,不如改成 “WHERE user_id < 123 OR user_id > 123”(如果业务允许),后者能用到索引。
- 模糊查询别用 “% 开头”
比如 “WHERE username LIKE '% 张 '”(查所有姓张的用户),会导致索引失效;而 “WHERE username LIKE ' 张 %'”(查姓张且以张开头的用户),能用到索引。
业务替代方案:如果需要 “全模糊查询”(比如搜索框),别用 MySQL 的 LIKE,改用 Elasticsearch—— 这能体现你懂技术选型,面试时会加分。
四、其他关键原则:细节决定性能
除了上述核心原则,这些细节也能体现你的实战经验,让面试官觉得 “你是真的用过 MySQL”。
- 主键默认用 “自增 ID”,别用 UUID
自增 ID 是连续的,插入数据时能顺序写入索引(B + 树结构),不会导致索引碎片;而 UUID 是随机字符串,插入时会打乱索引顺序,频繁导致索引页分裂,拖慢插入速度。
- 别建 “重复索引”,浪费空间
比如给 “user_id” 建了唯一索引,又建了普通索引 —— 这就是重复索引,不仅浪费磁盘空间,还会让更新操作变慢(需要同步更新两个索引)。
检查方法:用 “SHOW INDEX FROM 表名” 查看索引,避免同一字段建多个索引。
- 大表加索引要 “分批操作”,别影响业务
如果表有 1000 万条数据,直接用 “ALTER TABLE 表名 ADD INDEX 索引名 (字段名)” 加索引,会锁表,导致业务无法读写。
实战方案:用 “在线 DDL” 工具(比如 pt-online-schema-change),分批给表加索引,不锁表,不影响业务 —— 这一点提出来,面试官会觉得你有大表运维经验,很加分。
面试回答模板:把原则变成 “实战故事”
最后,给你一个面试回答模板,帮你把这些原则串成 “有场景、有问题、有解决方案” 的回答,比单纯背原则强 10 倍:
“MySQL 建索引,我总结了几个核心原则,都是从业务里踩坑总结的。比如我们做订单系统时,订单表有 1000 万条数据,一开始给‘create_time’建了单字段索引,查‘用户 123 近 30 天的订单’时很慢,后来才知道要建联合索引,而且要把‘user_id’放前面,因为‘user_id’过滤性更强,能快速定位到用户的订单;另外,我们还避免在索引字段上做函数操作,之前有次查‘2024 年 1 月的订单’,用了 DATE (create_time),导致索引失效,改成 BETWEEN 后速度快了 20 倍。
还有个细节是,大表加索引不能直接加,上次给用户表加索引,用了 pt-online-schema-change,没影响用户登录;主键我们用的是自增 ID,没敢用 UUID,因为怕索引碎片。这些原则的核心其实是‘以业务查询为导向,既要加速查询,又要避免拖慢写入’。”
这样的回答,既有原则,又有具体的业务案例和坑点,面试官一听就知道 “你是真的做过”,而不是背答案。
记住:MySQL 建索引不是 “按手册来就行”,而是 “跟着业务走”—— 你对业务查询越了解,建的索引就越高效,面试时也越有话可说。