mysql自增字段必须搭配主键或唯一索引使用,且仅支持整数类型;其值不连续,受插入失败、显式插入、重启等因素影响;修改起始值需大于当前最大值;分布式场景推荐uuid或snowflake。

MySQL 自增字段必须搭配主键或唯一索引使用
MySQL 的 auto_INCREMENT 不是独立属性,它依赖于索引约束才能正常工作。如果你只写 id int AUTO_INCREMENT 却没加 PRIMARY KEY 或 UNIQUE,建表会报错:Error 1075: Incorrect table definition; there can be only one auto-increment column and it must be defined as a key。
常见错误场景包括:
- 误以为只要声明了
AUTO_INCREMENT就能自增,忽略了索引要求 - 在已有表中用
ALTER TABLE ... MODIFY添加AUTO_INCREMENT时,忘记同步加PRIMARY KEY - 把
AUTO_INCREMENT加在非整数类型(如VARCHAR)上,直接失败
正确做法是:自增列必须是整数类型(TINYINT 到 BIGINT),且至少被一个 PRIMARY KEY 或 UNIQUE 索引覆盖。
主键自增不是“永远递增”,可能跳号或重复失败
MySQL 的 AUTO_INCREMENT 值分配发生在插入前,并不严格按事务提交顺序连续。一旦发生以下情况,就会跳过某些数字:
- 插入失败(如违反唯一约束、触发器抛错),但自增值已预分配
- 显式插入一个大于当前自增值的数(如
INSERT INTO t(id) VALUES(100)),后续自增将从 101 开始 - 服务器重启后,InnoDB 会扫描表找最大值再加 1(而不是读取内存中的计数器),导致“回退”或“跳号”
这不是 bug,而是设计权衡:牺牲连续性换取并发插入性能。所以不能假设 ID 是连续的,更不能用它做分页或范围判断依据。
修改自增起始值和步长要小心
通过 ALTER TABLE ... AUTO_INCREMENT = N 可以调整下一次插入的自增值,但注意:
- 设置值必须大于当前表中该列的最大值,否则会被 MySQL 忽略(不报错,也不生效)
-
innodb_autoinc_lock_mode参数影响并发行为:设为0(传统模式)会锁表,2(交错模式)则允许间隙,适合高并发但更易跳号 - 全局变量
auto_increment_increment和auto_increment_offset通常用于主从多节点环境避免冲突,单机慎改——改完会影响所有新表
SET GLOBAL auto_increment_increment = 2; SET GLOBAL auto_increment_offset = 1;
这种配置会让自增序列变成 1,3,5,…,但若中途改回 1,已有数据不会重排,只影响后续插入。
替代方案:UUID 或雪花 ID 更适合分布式场景
当业务需要全局唯一、可预测长度、不暴露插入顺序时,AUTO_INCREMENT 就不太合适。比如:
- 分库分表后,多个物理表无法共享自增序列
- 前端需要生成 ID(如预占订单号),不能等 INSERT 返回
- 审计要求 ID 不体现业务增长节奏(防爬虫估算规模)
这时可用:
-
UUID()函数生成字符串,但无序、占用空间大、索引性能差 - 应用层用 Snowflake 算法生成
BIGINT,时间有序、无中心依赖 - MySQL 8.0+ 支持
UUID_TO_BIN(UUID(), TRUE)存为BINARY(16),兼顾唯一性和存储效率
自增主键本身没问题,问题在于把它当成“通用唯一标识”来用——它只是 InnoDB 聚簇索引的物理组织手段,不是业务 ID。