MySQL 中 INSERT ... ON DUPLICATE KEY UPDATE 为什么会导致主键自增失效?

149 阅读2分钟

最近开发的过程中,使用ai生成代码,写了一条这样的SQL:INSERT ... ON DUPLICATE KEY UPDATE,然后发现一个奇怪的现象:

为什么使用这个语法后,自增主键(AUTO_INCREMENT)的值会跳跃甚至失效?

一、问题复现

假设我们有如下 users 表:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    code VARCHAR(50) UNIQUE,
    name VARCHAR(50),
    age INT
);

现在我们执行如下语句:


INSERT INTO users (id, code, name, age)
VALUES
    (NULL, 'yiyiwu', '依依吾', 99),
    (8, 'kekeke', '可可克', 100)
ON DUPLICATE KEY UPDATE
    code = VALUES(code),
    name = VALUES(name),
    age = VALUES(age);

运行后发现:

表里只有一条新的数据(id 是 10)

下一次插入时,主键跳到了 11,而不是 9

二、执行过程分析

来看一下每条数据的执行流程:

第一条:(NULL, 'yiyiwu', ...) id=NULL,触发自增主键

当前自增值为 10,则插入成功,id=10

自增值推进到 11

第二条:(8, 'kekeke', ...) 手动指定了主键 id=8,但如果 id=8 已存在,会触发主键冲突

执行 ON DUPLICATE KEY UPDATE 逻辑,改为更新操作

不插入,但仍然“尝试”执行 insert

自增值 不会倒退

三、MySQL 自增机制揭秘

MySQL 的自增策略:

在执行 INSERT 时,无论是否冲突,都先分配一个自增 ID

即使最后走的是 UPDATE,这个 ID 也被“浪费掉”了

所以你会发现:

自增主键中间有“缺口”

连续插入过程中 ID 会跳跃

四、解决方案建议

1. 不使用 INSERT ... ON DUPLICATE,改为“先查后插”:

SELECT id FROM users WHERE code = 'xxx';
-- 不存在再插入
INSERT INTO users (code, name, age) VALUES (...);

注意并发时需配合唯一索引或分布式锁。

2. 通过逻辑主键控制唯一性,而不是依赖自增主键

比如使用 code 作为业务唯一标识,而 id 仅作为内部排序标识。

看来ai写的代码有些时候并不可靠哇,还是要人工仔细审查的