前一阵和候选人聊到MySQL索引,对方能把八股文里的“最左前缀”“回表”“覆盖索引”背得很熟,但当我追问“InnoDB为什么选B+树,而不是哈希索引或者普通B树”的时候,他一下子卡住了。这个场景太常见了。很多人缺的不是索引知识点,而是把底层数据结构和算法串起来的能力。今天这篇内容专门聊聊MySQL索引的底层结构与查找算法,适合正在准备MySQL面试的人、日常写SQL的后端开发,以及被线上慢查询折腾过的运维同学。我会尽量从InnoDB存储引擎的视角来讲,不聊那些“背下来就行”的结论。
1. 先想明白:索引究竟“省”在哪
1.1 没有索引的全表扫描是拿IO硬扛
先说一个最简单的用户表,里面有两千万行,表结构大概是 id、username、phone 这种。如果执行 SELECT * FROM user WHERE username = 'abc',而且 username 上没有索引,MySQL能怎么做?答案是只能把整张表的数据页从前往后扫一遍,逐个判断username是否等于目标值。
InnoDB的数据并不是无序堆放的,它底层是按主键组织成一棵B+树,叶子页里面存完整行记录。但“按主键有序”只能帮主键查询,对username没有任何帮助。为了找一条username,MySQL需要遍历所有叶子页,也就是全表扫描。假如一行数据1KB,一个默认16KB的数据页大约能存16行,两千万行就有125万多个叶子数据页。这个数量级的页遍历下来,哪怕数据都在Buffer Pool里,也足够把CPU和内存带宽打满;如果遇到冷数据要读磁盘,那对IO的伤害就更直接了。
全表扫描和索引查找的区别,就像在一个没有目录的大厚书里找一句话,和直接翻到书后面的术语索引去查页码的区别。书后面的术语索引本身就是额外印出来的内容,它不包含正文全部信息,但能告诉你某个关键词在哪个页码,这就是索引最朴素的形态。
1.2 索引本质是一套“空间换时间”的有序冗余结构
索引不是玄学,它本质上就是额外维护一份数据,让查询能快速定位。但为什么说它“有序”很关键?因为有序结构才能用上二分定位、范围扫描、排序优化这些算法。如果索引只是把列复制一份,但内部没有规则,那和全表扫没有区别。
标题里写“时间结构”,大概率是想说“数据结构”。不过这个说法倒也不算错:索引优化的本质,就是用选择好的数据结构去换查询时间。面试官问“索引底层是什么”,其实想考察的是你对B+树的时间复杂度、树高、磁盘IO模型有没有概念。比如一棵三层的B+树,可以支撑两千万行级别的数据,而一次查找只需要两三次磁盘IO,这才是索引能快的真正原因。
但索引不是免费的。每建一个索引,写入时都要额外维护一棵树;每多一个索引,INSERT和UPDATE的成本都会增加,磁盘占用也变大。索引策略本质上是在“读的加速”和“写的成本”之间找平衡。所以不是每个字段都值得建索引,更不是索引越多越好。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. InnoDB的B+树到底是怎么组织数据的
2.1 数据页、索引页和树高
理解InnoDB索引,先要理解它的存储单元。InnoDB的数据是分成数据页来管理的,默认一个页16KB。B+树里的根节点、内部节点、叶子节点,在物理上都是一个一个的数据页。内部节点页里放的是“索引键 + 指向子页的指针”,叶子页里放的才是真正要查的东西。
为什么这个结构能扛住千万级数据?我们可以简单算一笔账。假设主键是BIGINT,占8字节,一个页指针大约占6字节,两者加起来约14字节。16KB的页大概能放下1170个这样的键指针组合,也就是说一个内部节点能分出约1170个子节点。假设一行完整记录约1KB,一个叶子页能放16行。
那么一棵两层的B+树,根节点下挂1170个叶子页,能存大约 1170 * 16 = 18720 行。三层的B+树,就能存大约 1170 * 1170 * 16 = 2190万 行。树高是三层的时候,查询一次最多只需要两次磁盘IO,因为根节点通常会常驻在InnoDB的Buffer Pool里。这就是千万级表按主键查依然很快的根本原因:不是CPU多快,而是IO次数被压缩到了两三次。
2.2 聚簇索引决定了主键比想象中更重要
InnoDB里有一个很反直觉的设计:表里的数据本身就是按主键组织成一棵B+树的,这棵树就是聚簇索引。普通二级索引的叶子节点只存“索引列 + 主键”,但聚簇索引的叶子节点存的是整行记录。换句话说,一张InnoDB表只能有一个聚簇索引,数据行的物理存储顺序也被主键顺序决定了。
如果你建表时没有指定主键,InnoDB会先找有没有非空的唯一键可以作为聚簇索引;如果也没有,它会在内部生成一个隐藏的row_id作为主键。虽然业务上好像不报错,但隐藏主键对二级索引不友好,而且你没法通过主键高效定位行。所以一张正规的InnoDB表,一定要显式设计主键。
聚簇索
