MySQL 数据迁移主键自增值陷阱
背景:目前需要对业务日志大表A进行迁移,只保留最近3个月的数据,遇到的问题是迁移完成后,新表B的磁盘空间占用没有减少。
MySQL 版本:8.0.36,InnoDB存储引擎。
注意:主键id是自增的,bigint类型
A表有3.6亿数据,磁盘占用 100GB,最近3个月的数据,约4000万数据,我们的方案是建新表B,然后将A表的数据导入进来。
表B结构和A表一样,但是未指定 AUTO_INCREMENT 的值,也就是默认从1开始。
操作步骤:
- 新建B表。
- 通过存储过程将A表最近3个月的数据导入B表(id是从300908629开始插入)。
- 交换表名完成数据迁移。
RENAME TABLE source_table TO source_table_old,source_table_new TO source_table;
存储过程如下:
1 | DELIMITER // |
数据导入完毕后,B表磁盘占用没有减少。
原因是:索引碎片化(物理存储不连续),当创建新表 B 时,若未显式指定 AUTO_INCREMENT 的起始值(默认为 1),但实际插入的数据主键 ID 从 3 亿开始。
InnoDB 会尝试按主键顺序存储数据,但由于插入的 ID 远大于默认值(1),存储引擎需要预留大量空洞(Empty Pages) 来应对“理论上”的主键范围(1 ~ 3亿),导致物理存储不连续,空间利用率低。
所以导致,虽然逻辑数据量仅 4000 万,但物理磁盘占用仍接近原表(100GB)。
解决方案:
方法一:建表时显式指定 AUTO_INCREMENT(推荐)
1 | CREATE TABLE B ( |
通过 CREATE TABLE ... AUTO_INCREMENT=300000000 明确告知 InnoDB 主键起始值,存储引擎会跳过预留空洞,直接从 3 亿开始分配物理页。数据物理存储连续,无碎片,因此空间占用会降至合理范围。
方法二:重建表优化空间(会锁表,谨慎使用)
OPTIMIZE TABLE = (ALTER TABLE B ENGINE=InnoDB) + (analyze table)
1 | -- 重建表(InnoDB 会重新整理物理存储) |
最后,对于一直有增量的数据表进行迁移,建议使用开源的一些工具进行数据迁移,如:Github 开源的 gh-ost