自增主键可从根本上避免页分裂,因其保证insert始终追加到b+树最右页,提升页填充率与i/o效率;uuid等随机主键则因插队导致频繁页分裂和碎片;改造大表需分步添加新自增主键列并更新应用写入逻辑。

主键用自增ID,比任何后期整理都管用。 页分裂导致的索引空洞本质是随机写入破坏B+树页内顺序,靠OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB只能治标——重建完下次INSERT又裂开。真正有效的解法是从源头控制插入顺序。
为什么UUID或随机主键必然引发页分裂
InnoDB的B+树叶子节点按主键逻辑排序,每页默认16KB。当主键无序(如UUID)时:
- 新记录大概率无法追加到当前最右页,而要“插队”进中间某页
- 目标页若已满,InnoDB必须分裂:原页保留约50%记录,另一半+新记录挪到新页
- 分裂后两页都未填满,且物理位置不连续,形成页级碎片和逻辑/物理顺序错位
- 这种碎片
DATA_FREE能反映一部分,但页内稀疏(如一页只存40条而非80条)无法被该字段捕获
自增主键如何避免页分裂
自增ID保证严格递增,INSERT始终追加到B+树最右叶子节点:
- 只要最右页有空间,就直接追加,不触发分裂
- 最右页填满后,一次性分裂成两个半满页,但后续插入仍持续向新页追加,整体页填充率高
- 数据在磁盘上物理连续性更好,范围查询、全表扫描I/O效率明显提升
- 注意:事务回滚导致ID跳空(如INSERT后ROLLBACK)不会造成页分裂,只是留下逻辑间隙,不影响页内结构
已有表改用自增主键的实操要点
对已存在UUID主键的大表,不能直接ALTER TABLE ... MODIFY id BIGINT AUTO_INCREMENT——这会失败或丢失数据。必须分步:
- 添加新列:
ALTER TABLE t ADD COLUMN new_id BIGINT UNSIGNED AUTO_INCREMENT FIRST, ADD PRIMARY KEY (new_id)(需确保无外键依赖) - 若原主键被其他表外键引用,先删外键,改完再重建
- 旧UUID列可转为普通索引或归档后删除,避免冗余存储放大碎片
- 执行后务必运行
ANALYZE TABLE t更新统计信息,否则优化器可能仍走旧索引路径 - 大表操作前,确认
innodb_file_per_table = ON,否则ALTER无法释放空间回操作系统
Online DDL期间页分裂是否暂停
不会暂停,但影响可控:
- MySQL 5.6+的Online DDL在拷贝数据阶段仍允许DML,新写入的数据会被记录到row log,并在最后一步重放
- 这意味着重建过程中,新INSERT仍会按原主键规则走(比如继续往UUID表里插),可能产生新分裂
- 所以
ALTER TABLE t ENGINE=InnoDB完成时,你得到的是“重建时刻”的紧凑结构,不是未来长期的保障 - 真正需要的是:重建后立刻切换应用写入逻辑,改用新自增主键,否则空洞会快速复现
页分裂产生的空洞藏得深——DATA_FREE可能为0,但SHOW INDEX FROM t里看到的Cardinality异常偏低,或EXPLAIN FORMAT=JSON中rows_examined_per_scan远高于实际行数,都是页内稀疏的信号。这时候光重建没用,得先揪出谁在乱插数据。











