先看现象。订单表主键改成 UUID 后跑 MySQL 8.0,插入 TPS 从 8000 掉到 1500,Innodb_buffer_pool_pages_free 一直往下走,information_schema 里表的 data_length 比实际行数对应的量大了两倍。

原因不止一个,得分开看。随机 UUID 让聚簇索引的插入点散落,每次写都可能触发页分裂。还有一种是主键是 36 字节字符串,二级索引都要带一份,索引整体膨胀。

不容易想到的是,页填充因子被分裂拉到 50%,同样的行数占用双倍页。

还有一种情况,没有用有序 UUID,InnoDB 的插入缓冲帮不上忙。

量化页分裂和索引膨胀

mysql -e 'SELECT table_name,data_length,index_length FROM information_schema.tables WHERE table_schema = "shop"'

再往下看缓冲池的空闲页走向

mysql -e 'SHOW GLOBAL STATUS' | grep Innodb_buffer_pool_pages

然后改成有序 UUID 或加自增主键再回填

mysql -e 'ALTER TABLE orders ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY FIRST'

剩下的就是重建表让页重新紧凑

mysql -e 'ALTER TABLE orders ENGINE=InnoDB'

怎么算真的好了,看的是同一批现象。插入 TPS 回到 6000 以上,表的 data_length 下降到原来的六成,页分裂导致的写放大明显减少。

UUID 换掉前要评估所有关联表的外键和业务代码;必须用 UUID 时选 v7 这类带时间前缀的有序版本。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189