联合索引是按字段顺序构建的单棵b+树,非多个单列索引叠加;其高效依赖最左前缀匹配原则——查询必须从最左列连续使用,跳过或缺失首列将导致索引失效或部分失效,且order by和分页也需严格匹配索引前缀顺序才能避免filesort。

联合索引不是“多建几个索引”的简单叠加,而是按字段顺序构建的一棵B+树。真正让它大幅提升复杂查询效率的,是它能一次性定位满足多个条件的数据范围——前提是索引设计与查询模式严格对齐。
联合索引字段顺序必须贴合真实查询高频路径
比如订单表中,90%的查询都带 tenant_id = ? AND status = ? AND created_at > ?,那索引就该建为 INDEX(tenant_id, status, created_at)。不能因为 created_at 区分度高就把它放最左——一旦 WHERE 缺了 tenant_id,整个索引就失效,MySQL 只能全表扫描。
- 业务中按租户隔离的场景,
tenant_id几乎总是查询起点,它必须放在最左 -
status是常用等值过滤条件,适合放第二位 -
created_at多用于范围查询(如BETWEEN或>),放最后可让它参与过滤,但不破坏前两列的精确定位能力
WHERE 条件必须从最左列开始连续使用
MySQL 的最左匹配原则是硬性规则:只有从索引最左列开始、连续覆盖的条件才生效。中间跳过或顺序错乱,都会导致部分甚至全部索引失效。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- ✅ 有效:
WHERE tenant_id = 1001 AND status = 'paid'→ 走前两列 - ✅ 半有效:
WHERE tenant_id = 1001 AND created_at > '2026-07-01'→ 只用上tenant_id,status和created_at无法参与索引查找 - ❌ 无效:
WHERE status = 'paid' AND created_at > '2026-07-01'→ 缺最左列tenant_id,索引完全不走
ORDER BY 和分页也得匹配索引前缀
排序和分页不是“附带功能”,它们同样依赖索引的有序结构。如果 ORDER BY 字段顺序与联合索引前缀不一致,MySQL 就得额外做 filesort,性能随数据量增长急剧恶化。
- 索引是
(tenant_id, status, created_at),ORDER BY tenant_id, status可直接利用索引排序 - 但
ORDER BY status, created_at即使 WHERE 有tenant_id,也无法复用索引排序——顺序不匹配,B+ 树不支持跳过首列再按后续列排序 - 深分页如
LIMIT 20 OFFSET 100000极度依赖有序扫描,一旦排序不走索引,偏移越大越慢
用 EXPLAIN 验证 Java 发出的 SQL 是否真走索引
Java 代码里写了 MyBatis XML 或拼了 SQL,不代表索引就生效。关键要看 MySQL 实际执行时是否选中了你建的索引。
- 开启
spring.jpa.show-sql=true或拦截日志,拿到 Java 实际发出的完整 SQL - 在 MySQL 客户端执行
EXPLAIN + 该 SQL - 看
key字段是否显示你的索引名;key_len是否符合预期(比如 INT=4,VARCHAR(50)≈202);Extra是否出现Using filesort或Using temporary
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










