MySQL 中说的“B树索引”,实际指的是 B+树索引,这是 InnoDB 和 MyISAM 存储引擎默认采用的索引结构。虽然官方文档和用户常统称其为“B树索引”,但底层实现严格来说是 B+树——它在磁盘 I/O 效率、范围查询和顺序访问方面比经典 B 树更适配数据库场景。
为什么 MySQL 用的是 B+树,不是 B 树?
核心原因在于数据库运行在磁盘上,而磁盘读写以“页”(page)为单位(InnoDB 默认 16KB)。B+树做了三项关键优化:
所有数据都存放在叶子节点,非叶子节点只存索引键(key),不存行记录指针或真实数据 → 同样大小的页能容纳更多 key,树更“矮胖”,高度通常 ≤3 层 叶子节点之间用双向链表相连 → 支持高效范围查询(如WHERE id BETWEEN 100 AND 200)和顺序扫描(
ORDER BY) 每次查找必走到底层叶子节点 → 查询路径长度固定,性能更稳定(不像 B 树可能在中间层就命中数据)
B+树索引怎么定位一条记录?
以主键索引为例(即聚集索引):
从根节点开始,按 key 大小做类似二分的比较,决定往哪个子节点走 逐层下探,直到抵达叶子节点 在叶子节点中精确匹配 key,拿到对应的数据页地址和行偏移量 InnoDB 直接读取该页中的完整行记录(因为主键索引的叶子节点存的就是整行数据)整个过程最多只需 2~3 次磁盘 I/O(取决于数据量),远优于全表扫描的 O(n) 成本。
普通索引(二级索引)和主键索引有什么区别?
关键差异在叶子节点存储内容:
主键索引(聚集索引):叶子节点存完整的数据行(聚簇方式组织数据) 普通索引(二级索引):叶子节点只存索引列值 + 对应主键值(不是物理地址);查到主键后,还需回表一次,用主键再去聚集索引里查完整行比如建了
INDEX idx_name ON users(name),执行
SELECT * FROM users WHERE name = 'Alice'就会先走二级索引找到主键 ID,再拿这个 ID 去主键索引里捞整行——这叫“回表查询”。若只查
SELECT id, name,且索引已覆盖,则无需回表(覆盖索引优化)。
什么时候 B+树索引会失效?
即使建了索引,以下情况仍可能导致全表扫描:
对索引列使用函数或表达式:WHERE YEAR(create_time) = 2025隐式类型转换:
WHERE phone = 13800138000(phone 是 VARCHAR,数字会触发转换) 模糊查询前导通配符:
WHERE name LIKE '%li'(无法利用索引的有序性) OR 条件中部分字段无索引:
WHERE a = 1 OR b = 2,若 b 列没索引,可能放弃整个索引 索引列参与计算:
WHERE score * 2 > 100
这些本质上都破坏了 B+树按 key 有序遍历的能力,使优化器选择放弃索引。
