auto_increment仅在insert时省略自增列、设为null或0时触发;若显式赋值则直接使用该值,不触发自增,且后续仍从当前最大值+1继续递增。

INSERT 时不填值,AUTO_INCREMENT 才会生效
只有当插入语句中明确不提供自增列的值(即设为 NULL、0,或直接省略该字段),MySQL 才会触发自增值生成逻辑。如果写了具体数字,哪怕只是 1,MySQL 就照单全收,不会干预。
常见错误现象:
- 执行
INSERT INTO users (id, name) VALUES (1, 'Alice');后,下一条INSERT INTO users (name) VALUES ('Bob');得到的id是 2 —— 看似正常,但其实是“巧合”,因为刚插过1;一旦中间插过100,后续就跳到101,不再连续 - 用 ORM(如 Django、Laravel Eloquent)时,若手动赋了
id字段,即使只在测试数据里写死,也会污染自增序列
建议做法:
- 建表时主键字段声明为
id INT PRIMARY KEY AUTO_INCREMENT,插入时完全不提id - 避免在应用层拼接 SQL 时把
id当普通字段处理 - 批量插入多行时,也别给
id列写值,让 MySQL 统一分配
ALTER TABLE ... AUTO_INCREMENT = N 不等于“重置”
执行 ALTER TABLE t AUTO_INCREMENT = 100; 只是把下一次分配的起点设为 100,并不会检查表里是否已有 id >= 100 的记录。如果已有 id = 150,那下一条插入仍会用 151,而不是报错或覆盖。
真正起作用的时机是:下一次插入且未指定 id 值时,MySQL 拿当前 AUTO_INCREMENT 值去分配,然后立即加一(按步长)存回元数据。
注意点:
- 这个命令对 MyISAM 表是即时生效并写入磁盘的;InnoDB 在 MySQL 5.7 及以前仅存在内存中,重启后会重新扫描最大
id+ 1;MySQL 8.0 起通过 redo log 持久化,重启不丢 - 不能用它来“清空并重排 ID”,比如想让所有记录从 1 开始编号——那是业务逻辑,得靠
CREATE TABLE AS SELECT+ 重命名,或导出再导入 - 如果表有外键引用,修改前先确认下游是否依赖 ID 连续性或具体数值
全局/会话级 auto_increment_increment 和 auto_increment_offset 影响所有新分配
这两个变量控制自增的“步长”和“起点偏移”,常用于主从架构或分库分表场景下避免主键冲突。例如双主写入时设 increment=2、offset=1 和 offset=2,就能让两台机器分别生成奇数和偶数 ID。
关键行为:
- 设置后,所有后续未指定
id的插入都按新规则算——不只是当前表,而是当前连接(会话级)或整个实例(全局级)下所有表 - 计算方式不是简单 “+1”,而是从
offset开始,每次加increment,取第一个 ≥ 当前最大id的值。例如当前最大id是 9,offset=2、increment=3,则候选序列为 2→5→8→11,选 11 - 查看当前值用
SHOW VARIABLES LIKE 'auto_increment%';,不是SHOW CREATE TABLE
事务回滚或唯一键冲突后,AUTO_INCREMENT 仍会前进
这是最容易被忽略的“不连续”根源。只要引擎开始分配了自增值,哪怕最终没插入成功,这个值也不会退还。
典型场景:
- 事务中执行
INSERT INTO t (name) VALUES ('test');,得到id=100,但随后ROLLBACK→ 下次插入就是101,不是100 - 表有唯一索引(如
UNIQUE KEY email),插入时因邮箱重复失败 → 自增已加到 100,但记录没进表 → 下次成功插入仍是101 - 并发插入相同唯一值,其中一个成功、其余失败 → 成功者拿走一个 ID,失败者各自也消耗了一个 ID
这不是 bug,是 InnoDB 为性能做的取舍:自增锁只在分配瞬间持有,不等事务结束。所以不要在业务逻辑里假设 ID 连续、可预测,尤其不能拿它做分页或排序依据。











