说实话,在此之前我也没想过这个问题“MySQL 自增 ID 用完了怎么办?”,大部分情况下平时建表都直接用 BIGINT,根本没想过这个问题。不过既然看到了,那我们就好好思考下。首先我们来拆接下面试官问这个问题的目的,或者说他想考察什么?我觉得他大概是想考察你对 MySQL 自增ID底层逻辑的理解和生产故障处理的应变能力。
MySQL自增ID用完了会发生什么?
MySQL 的自增 ID(AUTO_INCREMENT)是指MySQL自动生成唯一标识符。当一张表的自增列(通常为主键)达到了该列数据类型所能表示的最大值时,再插入新记录时,MySQL 无法再为下一行生成一个更大的唯一值,此时就会发生“自增 ID 用完了”的情况。
常说的自增 ID 分两种场景。第一种是我们显式给表设置了自增主键,比如用 INT 类型,有符号的最大值是 2147483647,也就是约 21 亿,无符号的是 4294967295 约 42 亿,当自增值达到这个上限后,下次插入生成的 ID 还是这个最大值,因为数据类型没法再存更大的数了,此时就会触发主键约束,报Duplicate entry 'xxx' for key 'PRIMARY'的错误,直接写不进去数据。第二种是表没有设主键,InnoDB 引擎会自动生成一个 6 字节的隐式 row_id 作为主键,这个 row_id 的最大值是 2^48-1,用完之后会直接归零重新递增,新插入的数据会覆盖同 row_id 的旧数据,这个坑非常多开发者都不知道,生产上踩中就是数据丢失的大事故。
MySQL整形数据取值范围如下图表:

梳理逻辑分阶段回答,条理更清晰
回答这个问题别一上来就说换 BIGINT,这样一听就感觉是背的八股文,不能体现面试者应对问题的逻辑思维能力,建议按故障处理的优先级分阶段说,这样才能体现你的问题处理逻辑。
第1阶段:应急处理,让业务先恢复
如果线上已经出现 ID 耗尽导致写入失败的故障,当务之急是先恢复业务。如果你的表存在大量已删除的历史数据,ID 空间有不少空闲段,可以直接执行ALTER TABLE your_table AUTO_INCREMENT = <max_id + 1>,复用之前被释放的 ID 空间,几分钟就能恢复写入能力。再实际操作前一定要先核对当前表内最大 ID 和已存在的 ID 范围,避免出现 ID 冲突,这个方案只能临时救急,不能作为长期解决方案。
第2阶段:短期扩展,彻底解决当前问题
应急之后要做根因修复,90% 的场景都是当初建表偷懒用了 INT 类型,直接把 ID 字段升级为 BIGINT UNSIGNED 就可以了,SQL 语句是ALTER TABLE your_table MODIFY id BIGINT UNSIGNED AUTO_INCREMENT,升级之后 ID 范围直接从 42 亿扩展到 18446744073709551615,也就是约 1800 亿亿,哪怕每秒写 10 万条数据,也要写 5000 多年才能用完,普通业务根本不可能碰到上限。
重点是,大表执行 ALTER 操作会锁表,因为要重构整个聚集索引的 B + 树,所有数据页都要调整,建议用 pt-online-schema-change 这类在线变更工具,避免长时间锁表影响线上业务,还要提前检查代码里有没有隐式将 ID 转为 INT 类型的逻辑,比如后端用 Integer、前端用 Number 接收的话都会出现溢出问题。
第3阶段:长期架构优化,适配分布式场景
如果你的业务规模已经大到 BIGINT 都可能不够用(一般是分布式高并发场景,单表单库不够用),就要从架构层面调整了。
- 可以采用分段 ID 生成策略,把 ID 空间按业务模块划分成多个段,每个段独立管理自增 ID,不用每次生成 ID 都请求数据库,性能更高;
- 也可以引入分布式 ID 生成服务,比如雪花算法,生成的 64 位 ID 自带时间戳、机器标识和序列号,天然有序,插入的时候不会导致 B + 树频繁页分裂,性能比 UUID 高很多;
- 如果业务已经到了单库瓶颈,也可以做分库分表,把数据水平拆分到多个实例,每个分片独立管理自增 ID,彻底规避单表 ID 耗尽的问题,但是分库分表会增加运维复杂渡,要根据业务实际规模选择,不要过度设计。
- 最后还是提一下 UUID 这个方案,虽然 UUID 几乎不可能耗尽,但是它是无序的,而且长度是 36 字节,比 BIGINT 大很多,插入的时候会导致聚集索引频繁页分裂,查询性能下降 20% 以上,只适合低并发的非核心场景。

预防才是最好的解决方案
其实如果出现自增 ID 耗尽的问题,一般要么是前期设计不规范导致的,要么是业务确实增长过快与当初设计有很大差异。所以做设计的时候可以做预防性设计,所以预防才是最好得解决办法。
- 如果建表的时候直接用 BIGINT UNSIGNED 就可以避免 99% 的问题,不要为了省 4 个字节的存储空间踩这么大的坑。
- 另外加上 ID 使用率监控,当 ID 使用率超过 70% 的时候就触发告警,提前处理,不要等故障发生了才救火。还有不用纠结自增 ID 是否连续,MySQL 本身就不保证自增 ID 连续,事务回滚、批量插入预留 ID 都会导致 ID 跳变,只要唯一就没问题,定期归档冷数据也可以减少 ID 的消耗。

如果面试时候回答一定要有逻辑,面试的时候按「应急止血→短期修复→长期架构优化→事前预防」的逻辑回答,条理清晰,面试官就知道你不是只会背八股,是真的有生产故障处理经验,就这一定就比大部分候选人强了,给人一种能抗事的感觉。
不知道你们遇到过什么有意思的面试问题呢?可以评论区留言说说。
网硕互联帮助中心





评论前必须登录!
注册