bigywater.tech
MySQL / InnoDB · 10

聚簇主键和二级索引:InnoDB 到底先查哪棵树

起本机MySQL 26.7单节点,用EXPLAIN FORMAT=TREE验证二级索引的叶子节点里到底存了什么——为什么查主键列不用回表,查其它列却要多访问一棵树。

Covering
查主键id不用回表
cost 10.3→35
查非索引列多付出的回表代价
两棵树
聚簇索引+二级索引各自的角色

做的事:一张10万行的表,两棵的树

建一张表,主键id,普通列k上建二级索引,插入10万行。

建表插入CREATE TABLE t1 (id INT PRIMARY KEY, k INT, v VARCHAR(50), INDEX idx_k(k)); 插入10万行,k = id % 1000(每个k值对应100行)

这张表有两棵 B+树:一棵是聚簇索引(按 id 排序,叶子节点直接存整行数据),一棵是二级索引 idx_k(按 k 排序,叶子节点存的是什么,这篇验证)。

验证:二级索引的叶子节点里到底存了什么

用 EXPLAIN FORMAT=TREE 分别查id、k、v这三列,看有没有触发"回表"。

结果EXPLAIN FORMAT=TREE SELECT id FROM t1 WHERE k=500 → Covering index lookup on t1 using idx_k (k = 500) (cost=10.3) EXPLAIN FORMAT=TREE SELECT k FROM t1 WHERE k=500 → Covering index lookup on t1 using idx_k (k = 500) (cost=10.3) EXPLAIN FORMAT=TREE SELECT v FROM t1 WHERE k=500 → Index lookup on t1 using idx_k (k = 500) (cost=35)

结果很直接:查 id(主键本身)和查 k(索引列本身)都显示 "Covering index lookup"——不用碰聚簇索引这棵树。只有查 v(既不是主键也不是索引列)才变成普通的 "Index lookup",cost 也从10.3涨到35。

这就证明了:二级索引的叶子节点存的是"索引列的值 + 主键值",不是行指针。既然主键值本身就已经存在二级索引的叶子里,那查询只要主键列,压根不用去聚簇索引里再找一次——这也是为什么 InnoDB 的主键要尽量选短小的类型:主键值会被复制进每一个二级索引的每一条记录里,主键越大,所有二级索引都会变大。

验证:查其它列时,第二棵树是怎么被访问的

当二级索引里没有你要的列,InnoDB 要做什么。

过程是:先在 idx_k 这棵树里用 k=500 做的范围/等值查找,找到符合条件的记录,每条记录里带着对应的主键值 id;然后拿这个 id,去聚簇索引那棵树里再做一次查找,取出这一行的完整数据(包括 v)。这两次的树查找,加起来才是一次"回表"查询的代价——这也是为什么 EXPLAIN 里能看到 cost 从 10.3 涨到 35,多出来的部分就是第二棵树查找的代价。

分步演示:二级索引叶子节点里存了什么

每一步都是本文上面用EXPLAIN FORMAT=TREE得到的原始结果。

查哪一列,决定了要不要碰第二棵树
这一步新验证的列

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),10万行两列索引表
    • EXPLAIN FORMAT=TREE:区分Covering index lookup与Index lookup
    京ICP备2026067502号-1京公网安备11010502063487号