# MySQL 单列索引,联合索引的存储区别? ## 一、单列索引与联合索引的核心结构差异(B+Tree 视角) InnoDB 的每一个索引(主键索引、二级索引)本质上都是一棵 **独立的 B+Tree**。 区别在于: ### 1. 单列索引(single-column index) #### B+Tree 的 key = 单一列字段 例如: ``` CREATE INDEX idx_name ON t(name); ``` InnoDB 构建的 B+Tree: ``` key: name value: 主键ID(主键索引的值) ``` 叶子节点存储: ``` (name, pk) ``` 特点: * key 比较简单 * 排序方式基于一个列 * 体积相对更小 --- ### 2. 联合索引(composite index / multi-column index) #### B+Tree 的 key = 按顺序拼接多个列 例如: ``` CREATE INDEX idx_user ON t(name, age); ``` InnoDB 构建的 B+Tree: ``` key: (name, age) value: 主键ID ``` 叶子节点存储: ``` (name, age, pk) ``` 关键点: * **索引 key 是“按列顺序拼接”后的二进制序列** * 排序规则:先按 name 排序,相同 name 再按 age 排序 * 比单列索引占用更多空间 * 能提高多条件查询效率(最左前缀原则) --- ## 二、物理存储结构上的三个重要区别 ### 区别 1:B+Tree 的“key”结构完全不同 #### 单列索引 key = 一个字段 #### 联合索引 key = 多个字段拼接后的排序 key 例如联合索引: ``` (name, age, create_time) ``` B+Tree 的排序顺序为: ``` (name asc, age asc, create_time asc) ``` 这是 InnoDB 固定行为,无法修改顺序。 --- ### 区别 2:二级索引的叶子节点 payload 不同(数据量差异) 所有 **二级索引** 的叶子节点都存放: * key(单列 key 或多列 key) * 主键值(PK) #### 单列索引叶子节点内容: ``` (name, pk) ``` #### 联合索引叶子节点内容: ``` (name, age, pk) ``` 加入字段越多,叶子节点变大 → 一个 page 能放的记录数变少 → B+Tree 更深 → 性能更差。 面试官会重点问这一点: > 为什么联合索引比单列索引更占空间? 因为叶子节点包含更多列。 --- ### 区别 3:回表路径不同 **单列索引:** 只能利用一个字段 查询: ``` WHERE name = 'A' AND age = 20 ``` 单列索引 idx_name 会: 1. 找到 name='A' 的 pk 列表 2. 再回表取 age 3. 然后过滤 age=20 成本较高。 --- **联合索引:** 已经包含 name + age 不需要回表过滤第二个字段,减少大量 I/O。 这就是为什么: ``` (name, age) ``` 比单列两个索引 `(name)` 和 `(age)` 更高效。 --- ## 三、联合索引的存储结构如何影响查询?(最左前缀原理来自哪里) 联合索引 `(name, age, city)` 的 B+Tree 实际结构: ``` key1: (name, age, city) leaf: (name, age, city, pk) ``` 排序方式类似: ``` (name1, age1, city1) (name1, age1, city2) (name1, age2, city1) (name2, age1, city1) ``` 因此可以连续使用的列必须从最左开始匹配。 ### 为什么不能用 age? 因为 tree 是按 name 排序的,age 分布是乱序的。 存储结构决定了: ``` (name, age) 可以走索引 (age) 不能走索引 ``` 最左前缀原则来自 **B+Tree key 的排序机制**,不是 MySQL 的“语法限制”。 --- ## 四、实际使用中会问你的“索引结构区别”要点总结 | 项目 | 单列索引 | 联合索引 | | -------------- | --------- | --------------------- | | B+Tree 数量 | 1 树 | 1 树 | | key 结构 | 一个列 | 多列拼接形成排序 key | | 叶子节点 | (col, pk) | (col1, col2, ..., pk) | | 空间消耗 | 小 | 大 | | 能否覆盖 where 多字段 | No | Yes(最左前缀) | | 回表 | 多 | 少 | | 适用场景 | 单字段过滤 | 组合查询、排序加速、覆盖索引 | --- ## 五、总结 > InnoDB 的单列索引和联合索引本质上都是独立的 B+Tree,只是存储的 key 不同。 > > 单列索引的 key 是一个列;联合索引的 key 是多个列按顺序拼接形成的复合排序键。 > > 因为联合索引的叶子节点包含更多列,所以占空间更大,但可以减少回表并加速复合查询。 > > 最左前缀原则本质上是联合索引的物理排序结构决定的。