先确认是否真耗尽:查show table status得auto_increment值,再select max(id),若二者接近才是真逼近上限;否则可能是自增值“虚高”。解决方法包括:1. 升级为bigint unsigned避免锁表与外键问题;2. 清理归档后重置auto_increment;3. 改用雪花算法等分布式id。

确认是不是真耗尽了,别被假警报骗了
看到 Duplicate entry '4294967295' for key 'PRIMARY' 就以为主键用完了?不一定。先查清楚:SHOW TABLE STATUS LIKE 'your_table' 看 Auto_increment 值,再执行 SELECT MAX(id) FROM your_table。如果两者接近(比如 Auto_increment 是 4294967295,MAX(id) 也是这个数),才是真逼近上限;如果 Auto_increment 远大于 MAX(id)(比如删过大量数据但没重置),说明是“虚高”,不是容量问题,而是自增值没回收。
ALTER TABLE MODIFY id BIGINT UNSIGNED 要小心这三件事
把 INT 改成 BIGINT UNSIGNED 是最常用解法,但线上执行不能只写一条 ALTER TABLE t MODIFY id BIGINT UNSIGNED AUTO_INCREMENT:
- 锁表风险:MySQL 5.7 或 8.0 早期版本对类型变更不支持
ALGORITHM=INSTANT,大表会锁写,建议用pt-online-schema-change或选业务低峰期 - 外键必须同步改:所有引用该
id的子表字段也得改成BIGINT,否则ALTER直接失败 - 应用层变量要检查:Java 里
Integer、Go 里int、Python 里int(虽无溢出但 ORM 映射可能截断)、MyBatis 的resultType、JSON 序列化逻辑,都可能因位宽不匹配丢数据
别为了“省空间”坚持用 INT,BIGINT 实际开销没那么吓人
有人担心 BIGINT 比 INT 多占 4 字节,亿级表会多几 GB 存储。但现实是:
- InnoDB 行格式下,主键本身是聚簇索引的一部分,额外 4 字节影响远小于想象——真正吃空间的是二级索引(因为每条二级索引记录都存主键值)
- 如果表有多个二级索引,改
BIGINT后所有二级索引行都变宽,需评估整体 B+ 树层级和内存缓存效率 - 对比分库分表或引入 Snowflake 的复杂度,
BIGINT是成本最低、兼容性最强的方案;BIGINT UNSIGNED最大值约18446744073709551615,按每秒 1 万插入算,撑 5000 年没问题
重置 Auto_increment 不等于“清空重来”,慎用
ALTER TABLE t AUTO_INCREMENT = N 看似能腾出空间,但有硬限制:
- 新值
N必须大于当前MAX(id),否则 MySQL 启动时会自动修正为MAX(id)+1 - 仅适用于可归档场景(如日志表),且必须确保旧数据已物理删除(
DELETE不释放空间,要用OPTIMIZE TABLE或TRUNCATE) - InnoDB 表的
Auto_increment值在重启后可能回退(尤其 MySQL 5.7 及之前),依赖它做“循环复用”极易引发冲突
真正容易被忽略的点:自增耗尽不是孤立故障,它往往暴露了监控缺失——上线前没设 Auto_increment 使用率告警(比如 >80% 就触发),等报错才发现,已经晚了。











