二级索引叶子节点存储索引列值和完整主键值(非指针),主键越长,索引体积越大、页数越多、树高增加、I/O上升;自增INT/BIGINT主键可控制长度、减少分裂、提升缓存与回表效率。

二级索引叶子节点里存的是主键值,不是指针
这是最常被误解的一点。InnoDB 的二级索引(比如 INDEX idx_name ON users(name))在叶子节点中,除了存储索引列值(name),**还完整存了一份主键值**。它不是存一个指向聚簇索引的“地址指针”,而是把整条主键数据原样复制过去。
所以主键越长,每条二级索引记录体积就越大;16KB 的数据页能装下的索引项就越少;同样数据量下,需要更多页来存二级索引 —— 这直接抬高了树高和磁盘 I/O 次数。
- 用
BIGINT主键(8 字节):一页约可存 1000+ 条二级索引记录 - 用
VARCHAR(64)主键(平均 40 字节):一页可能只剩 200–300 条 - 主键从 8 字节涨到 40 字节,同等数据量下二级索引页数可能翻倍
主键长度直接影响 B+ 树非叶子节点的扇出能力
非叶子节点只存“键值 + 子节点指针”,不存行数据,但键值大小仍由主键决定 —— 因为二级索引的非叶子节点存的是索引列值,而聚簇索引的非叶子节点存的是主键值。但真正压垮性能的是:主键越长,聚簇索引本身也变胖,进而拖累所有依赖它的操作。
更关键的是,**主键长度放大了所有二级索引的冗余存储**。例如外键引用该表时,子表的外键索引也会被迫带上全部主键列;联合索引里只要包含主键字段,膨胀效应就叠加发生。
- 复合主键
(tenant_id, order_id)→ 每个二级索引叶子节点都多存两个字段,极易突破单页容量,触发额外分裂 -
innodb_page_size = 16KB是硬限制,无法通过配置绕过 - 前缀索引(如
name(10))只能缓解索引列本身长度,对主键冗余无效
自增主键不只是“顺序”,它让主键体积可控且稳定
UUID 或随机字符串主键不仅长(通常 36 字符 ≈ 36+ 字节),而且无序。这导致新记录总插在中间已满页里,频繁触发页分裂;每次分裂都要拷贝数据、更新父节点,甚至推高树高。而 AUTO_INCREMENT INT 或 BIGINT 主键:
- 固定长度(4 或 8 字节),空间可预期
- 插入始终追加在右侧,页填充分率高,分裂极少
- 即使业务要求全局唯一 ID,也建议另建
uuid字段并加索引,主键仍用自增
别低估“稳定”二字 —— 一旦主键长度浮动(比如 VARCHAR 实际存了 1~64 字节),缓冲池里缓存的页利用率会波动,LRU 效果打折,间接拖慢查询。
容易被忽略的隐性成本:回表与缓存效率
二级索引查到主键后,还要回到聚簇索引取完整行,这个过程叫“回表”。主键越长,意味着每次回表前定位聚簇索引节点时,B+ 树搜索路径上要比较的键值字节数越多,CPU 比较开销上升;同时,更大的主键让 Buffer Pool 单页缓存的索引项更少,热数据更容易被淘汰。
尤其在高并发点查场景下,看似只是“主键多占几十字节”,实际反映在 P99 延迟上可能是毫秒级差异 —— 而这几十字节,在千万级表里会放大成 GB 级的索引体积和不可忽视的 I/O 压力。



















