直接改int为bigint不可简单执行alter table,因大表会锁死业务并存在auto_increment不重置、索引变宽等风险;须用双写+灰度切流方案,并手动调高自增值。

直接改 INT 为 BIGINT 是可行的,但不是“执行一条 ALTER TABLE 就完事”——大表会锁死业务,且有隐性风险。
为什么 ALTER TABLE ... MODIFY id BIGINT 在大表上很危险
MySQL 对大表执行字段类型变更时,默认走拷表逻辑:新建临时表、逐行复制数据、重命名切换。这个过程会持有 S(共享)锁甚至 X(排他)锁,导致写入阻塞,读也可能被卡住(取决于隔离级别和引擎)。一张 5 亿行的表,拷表可能持续数小时,期间所有 INSERT/UPDATE 都在排队等待。
- 即使加了
ALGORITHM=INPLACE,对INT → BIGINT这类“扩大数值范围”的操作,InnoDB 仍可能拒绝原地修改,回退到拷表 -
gh-ost或pt-online-schema-change可绕过锁表,但需额外部署、校验、切换流程,不是“一键生效” - 改完后,
AUTO_INCREMENT值不会自动重置,如果原值已接近2147483647,新字段起始值仍是那个“溢出临界点”,下次插入仍报ER_DUP_ENTRY
改之前必须确认的三件事
不查清楚就动手,大概率改完立刻报错。
- 查当前
AUTO_INCREMENT值:SHOW TABLE STATUS LIKE 'your_table_name',看Auto_increment字段是否已 ≥2147483647 - 确认字段是否带
UNSIGNED:SELECT DATA_TYPE, COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_NAME='your_table_name' AND COLUMN_NAME='id'。若为int无符号,理论最大是4294967295;若有符号,上限就是2147483647 - 检查是否有外键引用该
id字段:如果有,MODIFY会失败,必须先删外键,改完再重建
线上大表安全升级的实操路径
核心思路:避免单点变更,用双写+灰度切流代替“一刀切”。
- 新建表
your_table_name_new,结构完全一致,仅将id改为BIGINT UNSIGNED AUTO_INCREMENT - 应用层改造:对新写入流量,同时写旧表和新表(双写),读仍走旧表;确保双写事务一致性(如用本地消息表或可靠队列)
- 等新表
AUTO_INCREMENT值自然增长到超过旧表当前值(例如旧表停在2147483647,新表已到2147483650),说明新表已“预热完成” - 切读:把读请求逐步切到新表;验证无误后,停写旧表,只写新表;最后归档旧表
改完后最容易被忽略的坑
很多人以为改完类型就高枕无忧,其实还有两个关键动作没做:
-
AUTO_INCREMENT值没手动调高:即使字段变BIGINT,MySQL 仍沿用旧的自增值。必须显式执行ALTER TABLE your_table_name_new AUTO_INCREMENT = 2147483648(或更高),否则下一条插入仍尝试用2147483647,直接冲突 - 索引大小变化未评估:
BIGINT主键会让二级索引变宽(因为 InnoDB 二级索引叶子节点存的是主键值),若表有多个二级索引,总存储空间和内存占用会上升 4 字节 × 行数 × 索引数,对缓存命中率有实际影响
真正棘手的从来不是“怎么改”,而是“改的时候业务不能抖动”——监控要提前拉满,双写逻辑要压测过失败场景,新表的 AUTO_INCREMENT 起始值必须比旧表当前值至少大 1。











