直接alter table modify id bigint会卡住,因mysql需重建整表并重排每行物理结构,锁表时间随数据量剧增;即使5.6+支持inplace,修改数值类型仍不适用,且外键、触发器或高频写入会加剧阻塞与超时。

为什么直接 ALTER TABLE MODIFY id BIGINT 会卡住?
因为 MySQL 默认的 ALTER TABLE 在修改列类型时会重建整张表,锁表时间与数据量正相关。一张几百 GB 的表执行 ALTER TABLE t MODIFY id BIGINT UNSIGNED AUTO_INCREMENT,可能持续数小时,期间写入完全阻塞。
更危险的是:如果表上有外键、触发器或正在被应用高频写入,该操作极易触发超时或主从延迟飙升。
- MySQL 5.6+ 支持
ALGORITHM=INPLACE,但仅限某些变更(如加索引),修改数值类型不支持原地升级 -
BIGINT占用 8 字节,INT占 4 字节,InnoDB 必须重排每行记录的物理结构 - 即使使用
pt-online-schema-change或gh-ost,也需确保id列无UNIQUE约束以外的索引依赖(比如联合索引里含id)
gh-ost 迁移时,AUTO_INCREMENT 值怎么同步?
gh-ost 不会自动继承原表的 AUTO_INCREMENT 值——它新建的影子表初始 AUTO_INCREMENT = 1。若不做处理,切流后新插入数据会从 1 开始,必然冲突。
正确做法是在迁移前手动设置影子表的起始值:
gh-ost \ --max-load='Threads_running=25' \ --critical-load='Threads_running=50' \ --chunk-size=1000 \ --throttle-control-replicas="replica-host" \ --database="db" \ --table="t" \ --alter="MODIFY id BIGINT UNSIGNED AUTO_INCREMENT" \ --assume-rbr \ --allow-on-master \ --cut-over=default \ --exact-rowcount \ --concurrent-rowcount \ --default-retries=120 \ --panic-flag-file=/tmp/ghost.panic.flag \ --postpone-cut-over-flag-file=/tmp/ghost.postpone.flag \ --initially-drop-ghost-table \ --initially-drop-old-table \ --execute
迁移完成后,立即执行:
ALTER TABLE _t_gho AUTO_INCREMENT = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA='db' AND TABLE_NAME='t');
否则切流瞬间就会因主键重复失败。
改完 BIGINT 后,外键和应用代码要动哪些地方?
改动不止在数据库层。忽略以下任一环节,上线后大概率报错:
-
FOREIGN KEY关联的子表字段也必须同步改为BIGINT,否则 INSERT/UPDATE 外键列会报ERROR 1215 (HY000): Cannot add or update a child row - Java 应用中,
int类型的 ID 字段必须升级为long;MyBatis 的resultMap和parameterType也要同步改,否则 JDBC 驱动可能截断高位 - PHP 中
intval()会把超过PHP_INT_MAX(通常为 2^63−1)的BIGINT强转成负数,应改用string接收再转GMP或bcmath - 前端 JavaScript 注意:
Number.MAX_SAFE_INTEGER是 2^53−1,BIGINT超过此范围会丢失精度,建议 API 返回 ID 为字符串
监控脚本里为什么要把 unsigned 和 signed 分开算?
因为 INT 是否带 UNSIGNED 直接决定上限:有符号 INT 最大是 2147483647,无符号是 4294967295。监控脚本若统一按 2147483647 算,对无符号表会误报“已用尽”。
关键判断逻辑来自 information_schema.COLUMNS.COLUMN_TYPE 字段内容:
- 当
DATA_TYPE = 'int'且COLUMN_TYPE LIKE '%unsigned%'→ 上限取4294967295 - 当
DATA_TYPE = 'int'且COLUMN_TYPE NOT LIKE '%unsigned%'→ 上限取2147483647 - 一旦发现
usage_ratio > 90,就得立刻启动迁移流程——从 90% 到 100% 的缓冲期,在高写入场景下可能只剩 1–2 周
真正容易被忽略的不是改类型本身,而是外键联动、应用层数据类型溢出、以及前端 JS 数值精度这三处隐性断裂点。











