数据在磁盘上,一次 IO 读一页(InnoDB 默认 16KB)。二叉树的层数等于树的深度,一千万条数据树高 24 层,就是 24 次磁盘 IO。
B+ 树把"一个节点存一个值"改成"一个节点存一页的值"。一页 16KB,每条索引记录大约十几字节,一页能放上千个 key,三到四层就能撑起上亿数据。
索引结构的选择,本质上是在减少磁盘 IO 次数。
哈希索引等值查询是 O(1),但:
where create_time > '2026-01-01' 用不上InnoDB 的自适应哈希索引是个例外——它是引擎自己加的,不用手动建。
InnoDB 的表本身就是按主键组织的 B+ 树,叶子节点存的是整行数据,这叫聚簇索引。
二级索引的叶子节点存的是主键值。所以:
select * from article where title = 'xxx';
先在 title 索引上找到主键,再拿主键回聚簇索引查整行,这就是"回表"。
如果只需要 id:
select id from article where title = 'xxx';
索引里就有,不用回表,叫覆盖索引。这也是为什么 select * 是个坏习惯。
建了 (a, b, c) 联合索引,相当于建了 (a)、(a, b)、(a, b, c) 三个索引。
-- 能用
where a = 1
where a = 1 and b = 2
where a = 1 and b = 2 and c = 3
where a = 1 and c = 3 -- 只用到 a
-- 不能用
where b = 2
where c = 3
where a = 1 and c = 3 里,c 用不上索引,因为 b 断了。范围查询之后的条件也失效:
where a = 1 and b > 2 and c = 3 -- c 用不上
where id + 1 = 10varchar 列写成 where phone = 13800000000(少了引号)like '%张三'or 连接了没有索引的列explain select * from article where title = 'xxx';
重点看 type(ref 比 range 好,all 是全表扫描)、key(实际用了哪个索引)、rows(预估扫描行数)、Extra(出现 Using filesort、Using temporary 就要注意了)。
索引不是越多越好:每个索引都是一棵额外的 B+ 树,写入时要同步维护。高频查询、区分度高、能覆盖常用字段的,才值得建。