You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多PC访问同一服务器数据库时,代码取Max值自增导致重复记录问题

解决多客户端插入时的重复主键问题

这个问题太典型了——你现在的逻辑是先查最大值再手动自增,单客户端跑完全没问题,但多PC并发操作时,就会触发「竞态条件」:两个客户端同时读到相同的max值,各自自增后插入,自然就产生重复记录了。

下面给你几个靠谱的解决方案,按推荐优先级排序:

1. 让数据库自动生成主键(最推荐)

这是最简单也最可靠的方案,把主键字段设置成数据库的自增字段(不同数据库语法略有差异):

  • MySQL/MariaDB:定义字段时用 INT PRIMARY KEY AUTO_INCREMENT
  • SQL Server:用 INT PRIMARY KEY IDENTITY(1,1)
  • PostgreSQL:用 SERIAL 或 IDENTITY 类型

之后插入数据时,完全不用管主键的值,数据库会自动帮你生成唯一的递增主键,彻底规避并发冲突。比如插入语句可以写成:

INSERT INTO your_table (field1, field2) VALUES (?, ?);

数据库会全权处理主键生成,根本不需要提前查max值。

2. 使用事务+锁表(适合无法修改主键类型的场景)

如果因为历史限制不能改用自增主键,那可以用事务+锁表的方式,确保同一时间只有一个客户端能执行「查max+插入」的操作:

BEGIN TRANSACTION;
-- 锁定整张表,阻止其他客户端的读写操作
LOCK TABLE your_table WRITE;
-- 获取当前主键最大值
SELECT MAX(primary_key) FROM your_table INTO @max_id;
-- 自增后执行插入
INSERT INTO your_table (primary_key, field1, field2) VALUES (@max_id + 1, ?, ?);
COMMIT;

不过这种方式会降低并发性能,锁表期间其他客户端的插入请求会被阻塞,所以只适合并发量不高的场景。

3. 使用乐观锁(适合高并发但冲突率低的场景)

乐观锁的思路是不主动加锁,而是在插入时校验当前最大值是否和之前查到的一致,一致就插入,不一致就重试:

  1. 先查询当前主键最大值:SELECT MAX(primary_key) FROM your_table,假设得到current_max
  2. 插入时带条件校验:
INSERT INTO your_table (primary_key, field1, field2)
SELECT current_max + 1, ?, ?
WHERE (SELECT MAX(primary_key) FROM your_table) = current_max;
  1. 检查插入是否成功(比如看SQL执行后的影响行数),如果影响行数为0,说明已有其他客户端插入了新数据,此时重试整个流程(重新查max再插入)

这种方式不会阻塞其他操作,并发性能好,但冲突率高的话会有较多重试,需要在代码里处理重试逻辑。

补充:你的原逻辑出问题的核心原因

举个直观的例子:

客户端A执行SELECT MAX(id)得到10,准备插入11
同一时间客户端B也执行SELECT MAX(id),此时A还没完成插入,所以B也拿到10
随后A插入11,B也插入11,重复主键就产生了

这就是典型的非原子操作漏洞——「查max」和「插入」是两个独立步骤,中间的时间间隙被其他客户端钻了空子。

内容的提问来源于stack exchange,提问作者Adham Hatem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:00:19