当INT主键接近上限时,需先检查Auto_increment值、MAX(id)、删除插入影响、引擎类型;再安全升级为BIGINT,同步更新外键、应用层变量、ORM及缓存日志链路,并评估分库分表必要性。

MySQL自增主键溢出前必须做的检查
当 INT UNSIGNED 主键接近 4294967295、或 INT SIGNED 接近 2147483647 时,插入会直接报错 ERROR 1062 (23000): Duplicate entry '2147483647' for key 'PRIMARY'——这不是重复值,是自增器卡在最大值后反复回绕。别急着改类型,先确认真实瓶颈:是否真由主键耗尽导致?
- 查当前自增值:
SHOW TABLE STATUS LIKE 'your_table';看Auto_increment字段 - 查最大已有 ID:
SELECT MAX(id) FROM your_table;对比是否接近上限 - 检查是否有大量删除+插入导致
Auto_increment虚高(例如批量导入后删了 90% 数据,但自增未重置) - 确认表引擎是
InnoDB(MyISAM 的自增行为不同,且不支持事务级安全)
ALTER COLUMN 到 BIGINT 的实操要点
把 INT 改成 BIGINT 是最直接的解法,但线上表不能只执行一条 ALTER TABLE t MODIFY id BIGINT UNSIGNED AUTO_INCREMENT; 就完事。
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
- DDL 会锁表(尤其大表),建议用
pt-online-schema-change或 MySQL 8.0+ 的ALGORITHM=INSTANT(仅限某些修改,类型变更通常不支持 INSTANT) -
BIGINT UNSIGNED最大值是 18446744073709551615,够用几十年,但会多占 4 字节/行——对亿级宽表,总存储和内存开销需重新评估 - 外键约束必须同步更新:关联该主键的所有子表字段也要改成
BIGINT,否则ALTER会失败 - 应用层检查所有
int类型变量(如 Java 的Integer、Go 的int)、ORM 映射(如 MyBatis 的resultType)、JSON 序列化逻辑,避免截断或解析异常
分库分表不是“主键不够”的万能解药
如果单表已超 5000 万行、QPS 持续 >2000,或业务上天然存在多租户隔离需求(如 SaaS),才值得引入分库分表。只为解决主键耗尽而分片,反而增加复杂度和故障面。
- 分片键选错会导致热点:比如用
user_id分片,但某大客户订单量占全量 40%,所有写请求都打到同一分片 - 全局唯一 ID 不能依赖自增:得换方案,如
TinyID、Leaf、Snowflake 或数据库号段模式,这些服务本身也有容量上限,要提前压测 - 跨分片
JOIN、ORDER BY、GROUP BY无法下推,应用层要聚合,延迟和内存消耗陡增 - 备份、DDL、数据迁移难度指数上升——一个
ALTER变成 N 个库各执行一次,还要保证顺序和一致性
真正容易被忽略的细节
主键类型变更后,最容易翻车的地方不在数据库,而在缓存和日志链路。
- Redis 缓存 key 若拼接了
id(如order:123),没问题;但若用了order:000000123这类固定长度格式,BIGINT后位数变长,旧 key 失效且无法自动迁移 - Elasticsearch 或 ClickHouse 同步任务,若用
id > last_sync_id增量拉取,last_sync_id存储字段类型没升级,会溢出变负数,同步直接中断 - Binlog 解析服务(如 Canal)消费时,若用整型反序列化
id字段,而下游仍是int32,同样会截断——这问题上线后才暴露,排查极难 - 监控告警规则里硬编码的
MAX(id) 条件,需要同步更新阈值

















