自增主键耗尽会触发error 1062,本质是int unsigned达上限4294967295后持续尝试插入同一值;需通过select max(id), auto_increment和describe查当前使用率与类型,超70%即高危,可临时重置auto_increment或升级bigint,但后者需应对锁表与外键等风险。

自增主键耗尽不是“会不会发生”的问题,而是“什么时候发生、影响多大”的问题——只要用的是 INT UNSIGNED 且日均写入超万级,3~5 年内大概率触发 ERROR 1062: Duplicate entry '4294967295' for key 'PRIMARY',此时写入已完全卡死。
怎么快速确认是不是真快用完了?
别等报错。先查两个关键值是否逼近上限:
-
SELECT MAX(id), AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';—— 如果两者都 > 30 亿(INT UNSIGNED上限是 4294967295),风险极高 -
DESCRIBE your_table;看id列的Type:是int(11) unsigned还是更小的tinyint/smallint?前者撑到 42 亿,后者可能几万条就撞墙 - 注意:
AUTO_INCREMENT值虚高(比如MAX(id)是 500 万但AUTO_INCREMENT显示 1000 万)说明有大量删除,存在空洞,可先重置缓解
ALTER TABLE MODIFY id BIGINT 能不能直接上?
能,但不是“执行完就安全了”。真正卡住你的,是 InnoDB 重建聚簇索引的过程,不是语句本身:
- 5000 万行以上宽表,
ALTER TABLE your_table MODIFY id BIGINT UNSIGNED AUTO_INCREMENT可能锁表数小时,期间所有INSERT/UPDATE都阻塞 - MySQL 8.0 的
ALGORITHM=INSTANT对类型变更无效;INPLACE也大概率 fallback 到拷贝模式,SHOW PROCESSLIST里看到copy to tmp table就说明正在重写全量数据 - 如果表有外键,必须先
DROP FOREIGN KEY,改完再加回,否则直接报ERROR 1832 - 改完后立刻验证:
INSERT一条新记录,再查LAST_INSERT_ID()是否正常递增,别只看 DDL 成功就以为万事大吉
不改类型,还能续命吗?
可以,但属于临时止血,适合无法停机、或需争取灰度时间的场景:
-
ALTER TABLE your_table AUTO_INCREMENT = N:N 必须严格大于当前MAX(id),例如MAX(id) = 5000000,则设为5000001;该操作毫秒级完成,只改内存+字典计数器 - 归档冷数据后
OPTIMIZE TABLE:删掉created_at 的历史数据,再执行 <code>OPTIMIZE TABLE,它会重置AUTO_INCREMENT基值并释放空间,但同样会锁表 - 调高
innodb_autoinc_lock_mode = 2(默认值):减少并发插入时的自增锁争用,提升稳定性,但不解决根本容量问题 - 禁用
INSERT IGNORE或ON DUPLICATE KEY UPDATE:它们在主键冲突时静默失败,数据可能丢失而不报警
为什么改完 BIGINT 还可能出事?
很多人忽略链路中非数据库环节的适配成本:
- 应用层变量类型没改:Java 的
Integer接收 19 位 ID 会溢出,Go 的int32同理;ORM 映射(如 MyBatis 的<result property="id" column="id"></result>)也要同步改 - Redis 缓存 key 用
"user:" + id,没问题;但若存的是 JSON,JavaScript 默认把超过 2^53 的整数转成浮点,精度丢失,9223372036854775807可能变成9223372036854776000 - ELK 日志里记录的 SQL、监控系统中基于
id的聚合指标,都要确认是否支持 19 位数字解析 - 外键子表字段仍是
INT?那主表升了BIGINT,子表插入时就会报错,必须一起改
最易被跳过的动作是查清所有外键依赖和应用层类型定义——DDL 执行成功只是第一步,链路里任何一环没对齐,都会在某个凌晨三点爆发主键冲突。











