自增主键用尽了怎么办?INT溢出、在线迁移与预防策略全解析

举报
这个DBA有点耶 发表于 2026/09/16 16:26:42 2026/09/16
【摘要】 MySQL INT自增主键用尽是很多团队迟早会面对的问题。INT有符号上限约21亿,无符号约42亿。当AUTO_INCREMENT值触达类型上限后,后续INSERT将触发主键冲突,整库写入停服。本文从溢出机制、元数据监控、在线迁移方案三个层面,给出完整的技术方案与避坑指南。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

前两天在技术群里看到一条消息:“生产环境订单表插入报错了,Duplicate entry '2147483647' for key 'PRIMARY'。”

这是典型的INT自增主键溢出

在展开之前,先把几个关键概念说清楚。

什么是自增主键? 主键是表中唯一标识每一行记录的字段。自增主键(AUTO_INCREMENT)是MySQL的一种机制——每次插入新记录时,数据库自动为这个字段分配一个递增的整数值,不需要应用层手动指定。好处是简单、高效、天然保证唯一性。

什么是主键溢出? 每种数据类型都有取值上限。INT类型用4个字节存储,有符号时最大值为2,147,483,647(约21亿),无符号时最大值为4,294,967,295(约42亿)。当自增主键的值达到这个上限后,InnoDB内部的计数器不会自动循环,后续INSERT操作会持续触发ER_DUP_ENTRY错误,导致整库写入停服

为什么这个问题越来越常见? 很多团队在项目初期用INT做主键,认为“21亿够用了”。但对于日均写入百万行的订单表、日志表、流水表,21亿在几年内就可能触达。更关键的是,很多团队没有监控自增ID消耗进度的习惯,等到报错了才发现——而这时候留给迁移准备的时间窗口可能只有数周。

一、怎么提前发现?

在问题爆发之前,可以主动监控ID消耗进度。通过查询information_schema,可以精准获取各表的AUTO_INCREMENT值和类型上限的比例:

SELECT 
    t.TABLE_SCHEMA,
    t.TABLE_NAME,
    c.DATA_TYPE,
    t.AUTO_INCREMENT,
    CASE 
        WHEN c.DATA_TYPE = 'int' AND c.COLUMN_TYPE NOT LIKE '%unsigned%' THEN 2147483647
        WHEN c.DATA_TYPE = 'int' AND c.COLUMN_TYPE LIKE '%unsigned%' THEN 4294967295
        WHEN c.DATA_TYPE = 'bigint' THEN 9223372036854775807
    END AS MAX_VALUE,
    (t.AUTO_INCREMENT / CASE 
        WHEN c.DATA_TYPE = 'int' AND c.COLUMN_TYPE NOT LIKE '%unsigned%' THEN 2147483647
        WHEN c.DATA_TYPE = 'int' AND c.COLUMN_TYPE LIKE '%unsigned%' THEN 4294967295
        ELSE 9223372036854775807
    END) * 100 AS usage_ratio
FROM information_schema.TABLES t
JOIN information_schema.COLUMNS c 
    ON t.TABLE_SCHEMA = c.TABLE_SCHEMA 
    AND t.TABLE_NAME = c.TABLE_NAME
WHERE c.EXTRA = 'auto_increment'
    AND t.AUTO_INCREMENT IS NOT NULL
HAVING usage_ratio > 80;

监控阈值建议:80%触发预警,90%触发紧急告警。在海量写入场景下,从90%到100%的窗口期可能只有数周,留给迁移准备的时间非常有限。

二、在线迁移方案:INT → BIGINT

最直接的方案是把自增主键从INT升级为BIGINT。BIGINT占用8字节,有符号上限约922亿亿,足够绝大多数业务使用。

不能直接执行ALTER TABLE。修改数值类型会触发MySQL重建整张表,锁表时间与数据量正相关。几百GB的表执行ALTER TABLE t MODIFY id BIGINT,可能持续数小时,期间写入完全阻塞。即使MySQL 5.6+支持ALGORITHM=INPLACE,修改数值类型也不支持原地升级。

生产环境需要用gh-ost做无锁迁移。核心流程是:创建影子表 → 复制存量数据 → 通过Binlog增量同步 → 原子切换。

有几个容易踩的坑需要注意:

坑1:AUTO_INCREMENT值不会自动继承。 gh-ost新建的影子表初始AUTO_INCREMENT=1。如果不处理,切流后新插入数据会从1开始,必然冲突。正确做法是在迁移完成后立即同步:

ALTER TABLE _t_gho AUTO_INCREMENT = (
    SELECT AUTO_INCREMENT FROM information_schema.TABLES 
    WHERE TABLE_SCHEMA='db' AND TABLE_NAME='t'
);

坑2:外键关联的子表字段必须同步改为BIGINT。 忽略这一步会导致ERROR 1215

坑3:应用层代码需要同步升级。 Java应用中int类型的ID字段必须升级为long,ORM映射也需要调整。

三、更彻底的方案:分布式ID

如果团队已经在做分库分表,或者业务量增长极快,INT迁移到BIGINT可能只是“续命几年”。更彻底的方案是逐步废弃自增主键,改用分布式ID生成方案

常见方案包括:雪花算法(Snowflake)、号段模式(如美团Leaf)、以及基于数据库的全局序列。

金仓KES Sharding内置了全局序列能力,提供高性能、无冲突的全局唯一ID生成机制。对于从集中式向分布式演进的银行核心系统来说,全局序列是分布式架构的基础组件之一——应用层不需要自己维护ID生成逻辑,数据库层面统一提供。

四、预防比迁移更重要

如果你的系统还没到21亿,现在就是最好的预防时机。

新建表时直接用BIGINT。 BIGINT只比INT多占4字节存储空间,但对于避免未来的迁移成本来说,这点开销可以忽略不计。

对于已上线的INT表,把上面的监控脚本加到日常巡检中。接近80%时提前规划迁移方案,不要等到报错了才行动。

五、小结

自增主键溢出是一个“看起来很远、实际很近”的问题。INT的21亿上限,对于日均百万写入的系统,可能就是几年的光景。提前监控、提前规划在线迁移方案、新建表直接用BIGINT——这三件事做好,就能避免被一个主键类型卡住整条业务线。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。