SQL并发更新冲突指的是多个事务同时对同一行数据进行更新操作时,由于操作执行顺序交叉导致的数据异常问题,常见的表现有更新丢失、脏写等,会直接影响业务数据的准确性。

常见并发更新冲突场景
最典型的冲突场景是更新丢失,比如两个事务同时读取某条商品库存记录,库存初始值为10,事务A读取后准备扣减1,事务B也读取后扣减2,两个事务先后提交,最终库存可能只被扣减了其中一个值,而不是预期的3,导致数据错误。
核心处理策略
1. 乐观锁策略
乐观锁假设并发冲突发生概率较低,更新时不先加锁,而是在提交更新时判断数据是否被其他事务修改过。常见实现方式是给表增加版本号字段或者更新时间戳字段。
以版本号为例,表结构如下:
-- 商品表结构示例
CREATE TABLE product (
id INT PRIMARY KEY,
stock INT NOT NULL,
version INT NOT NULL DEFAULT 0
);
更新时的SQL逻辑为:
-- 先查询当前版本号和库存 SELECT stock, version FROM product WHERE id = 1; -- 假设查询到的version是0,stock是10,执行更新 UPDATE product SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 0;
如果更新语句返回的影响行数为0,说明版本号已经被其他事务修改,当前更新冲突,需要重试或者提示用户。
2. 悲观锁策略
悲观锁假设并发冲突发生概率较高,在读取数据时就直接加锁,阻止其他事务同时修改该数据,直到当前事务提交后锁才会释放。在SQL中可以通过FOR UPDATE语句实现行级悲观锁。
使用示例:
-- 开启事务 START TRANSACTION; -- 查询并加行锁 SELECT stock FROM product WHERE id = 1 FOR UPDATE; -- 执行更新操作 UPDATE product SET stock = stock - 1 WHERE id = 1; -- 提交事务,释放锁 COMMIT;
注意悲观锁会阻塞其他事务的更新操作,高并发场景下可能影响系统吞吐量,适合冲突频繁或者更新逻辑复杂的场景。
3. 调整事务隔离级别
数据库的事务隔离级别也会影响并发更新的表现,默认的可重复读隔离级别在部分数据库中可以避免更新丢失问题,比如MySQL的InnoDB引擎在可重复读级别下,会通过MVCC和间隙锁机制避免更新冲突。如果需要更严格的冲突控制,可以调整为串行化隔离级别,但会大幅降低并发性能。
设置事务隔离级别的SQL示例:
-- 设置当前会话的事务隔离级别为可重复读 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
策略选择建议
不同策略的适用场景不同,可以参考以下维度选择:
- 如果并发量低、冲突概率小,优先选择乐观锁,性能开销更小
- 如果并发量高、冲突频繁,优先选择悲观锁,避免大量重试带来的性能损耗
- 如果业务对数据一致性要求极高,可以结合调整事务隔离级别使用
| 策略类型 | 适用场景 | 性能影响 |
|---|---|---|
| 乐观锁 | 低并发、冲突少 | 小 |
| 悲观锁 | 高并发、冲突多 | 中 |
| 隔离级别调整 | 高一致性要求 | 大 |
注意事项
使用乐观锁时要注意重试逻辑的设计,避免无限重试导致系统资源耗尽,一般可以设置最大重试次数。使用悲观锁时要注意锁的范围,尽量缩小FOR UPDATE的查询范围,避免锁住过多行影响其他业务。另外所有涉及并发更新的事务要尽量简短,减少锁持有时间,提升系统整体并发能力。