随机uuid破坏b+树页物理连续性:因其离散插入导致频繁50-50页分裂,引发不可控新页分配、两次随机io、父节点更新及连锁分裂,实测百万行页分裂达8000次、填充率低于70%。

随机UUID破坏B+树页的物理连续性
InnoDB 的聚簇索引就是表数据本身,主键值直接决定行数据在磁盘上的物理存放顺序。B+树叶子节点是16KB的Page,每页存多行,页内按主键排序,页与页之间用双向链表连接。自增ID插入时,新记录总落在最后一块页末尾;而UUID()(v4)生成的是完全无序的128位随机值,插入位置在整棵树中均匀散落——大概率命中中间某页,且该页往往已满。
页分裂触发强制随机磁盘写入
当目标Page空间不足时,InnoDB必须执行50-50页分裂:申请一个全新物理页、把原页约一半数据复制过去、更新父节点指针、刷盘两个页。这个过程包含:
• 向操作系统申请新磁盘块(位置不可控,大概率不连续)
• 原页部分数据读出 + 新页写入(两次随机IO)
• 父节点索引项更新(可能引发上层页分裂,连锁反应)
• 所有变更需写入redo log并刷盘,进一步放大随机写压力
BINARY(16)存UUID也不解决根本问题
即使把UUID()转成BINARY(16)(用UUID_TO_BIN(uuid, 0)),只是省了连字符、减少存储体积,但字节序仍是随机的,无法改变插入位置的离散性。MySQL 8.0.31+的UUID_TO_BIN(uuid, 1)把时间戳高位左移,能带来一定时间局部性,但相比真正趋势递增的ULID或Snowflake,页分裂仍显著更多。实测显示,百万级表用纯随机UUID,SHOW ENGINE INNODB STATUS里频繁出现Pages split due to insert,页平均填充率常低于70%。
二级索引膨胀加剧IO放大效应
主键是CHAR(36)或BINARY(16),所有二级索引的叶子节点都得完整存储这个主键值。相比BIGINT主键,每个二级索引条目大2–4倍,导致:
• 单页能存的索引项更少,B+树高度增加,查询需更多次IO
• 缓冲池innodb_buffer_pool_size更难缓存住热点索引页
• 批量插入时,二级索引更新本身也触发额外页分裂
真正关键不是“UUID慢”,而是“随机性撞上了InnoDB聚簇索引的物理组织逻辑”。哪怕你用BINARY(16)、关掉innodb_checksum_algorithm、调大innodb_log_file_size,只要插入位置不收敛到末尾,页分裂和随机IO就躲不掉。











