必须查max(id)和auto_increment值,若二者均接近上限(如4294967295)才是真耗尽;否则为“虚高”,可临时调整auto_increment续命,但真耗尽须升级bigint并同步处理外键与应用层变量。

主键用完不是“还能撑几天”的问题,而是写入已实质中断——只要 AUTO_INCREMENT 卡在上限(比如 4294967295),后续所有 INSERT 都会报 ERROR 1062、ERROR 1467 或 ERROR 1690,业务写入立即失败。
怎么确认真耗尽了,而不是虚高?
别只看 SHOW CREATE TABLE 或 DESCRIBE,它们不反映运行时真实值。必须查两个关键数字:
-
SELECT MAX(id) FROM your_table:当前最大已用 ID -
SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table':MySQL 下次准备分配的 ID
如果两者都接近上限(如都是 4294967295),才是真耗尽;如果 AUTO_INCREMENT 远大于 MAX(id)(比如删过数据但没优化),属于“虚高”,可临时调整续命。
ALTER TABLE MODIFY id BIGINT UNSIGNED 必须注意这三点
这是最常用解法,但线上执行极易翻车:
- 锁表不可避免:MySQL 5.7/8.0 对整数类型扩宽(
INT→BIGINT)仍触发全表拷贝,大表可能锁写数小时;超 50GB 表建议用pt-online-schema-change - 外键必须同步改:所有引用该字段的子表也要
MODIFY对应列,否则ALTER直接报ERROR 1832;先SHOW CREATE TABLE找出外键名,再DROP FOREIGN KEY - 应用层变量要检查:Java 的
Integer、Go 的int、MyBatis 的resultType、JSON 序列化逻辑,都可能因位宽不匹配截断或溢出
临时续命只适用于虚高场景
当 MAX(id) 还没到上限(比如是 4294967290),但 AUTO_INCREMENT 已被推高到 4294967295(常因长事务或批量插入导致),可用:
-
ALTER TABLE your_table AUTO_INCREMENT = 4294967296—— 注意:值必须 > 当前MAX(id),否则 MySQL 启动时自动修正为MAX(id)+1 - 该操作不重建表,秒级完成,但仅治标;必须配合归档冷数据 +
OPTIMIZE TABLE才能真正回收空间并稳定自增值 - 不能用于真耗尽:若
MAX(id)已是4294967295,设成4294967296会直接报错
BIGINT 不是万能解药,但比 UUID 更靠谱
有人想换 UUID 规避溢出,但代价明显:
-
UUID()生成的CHAR(36)主键会让所有二级索引变宽(每条记录存 36 字节而非 8 字节),B+ 树层级加深、缓存效率下降、JOIN 性能受损 -
BIGINT UNSIGNED最大值约18446744073709551615,按每秒 1 万插入算,够用 5000 年;实际存储开销远小于直觉——InnoDB 聚簇索引下,多 4 字节影响有限,真正吃空间的是二级索引 - 真正复杂的是分库分表或雪花算法,而
BIGINT是兼容性最强、改造成本最低的方案
最易被忽略的点:MySQL 8.0 持久化了 AUTO_INCREMENT 值,重启不再靠 MAX(id)+1 重建——这意味着一旦卡死,就真的卡死了,没法靠重启“蒙混过关”。











