高流量应用多连接下MySQL UPDATE并发异常的解决方法
解决MySQL高并发下UPDATE的库存超卖问题
你遇到的是典型的并发更新导致的库存超卖问题——两个请求同时读取到相同的库存值(5),各自执行减5操作后最终导致负数。以下是几种可靠的解决方案:
1. 在UPDATE语句中直接加入库存校验条件
最直接高效的方式是把库存充足的判断写进UPDATE的WHERE子句,让数据库层面保证只有库存足够时才执行更新:
UPDATE table SET balls=balls-5 WHERE marketId=3212 AND balls >=5;
执行后务必检查受影响的行数:
- 受影响行数为1:更新成功,库存正常扣减;
- 受影响行数为0:库存不足,直接返回用户“库存不足”提示即可。
这种方式利用MySQL的原子性保证操作一致性,无需额外事务或锁,性能最优。
2. 事务结合行级锁(悲观锁)
如果业务需要先读取库存做额外判断(比如用户权限校验),再执行更新,可以用事务+行级锁避免并发冲突:
START TRANSACTION; -- 锁定指定行,其他请求需等待当前事务提交后才能操作该行 SELECT balls FROM table WHERE marketId=3212 FOR UPDATE; -- 此处可加入业务逻辑判断(如用户是否有购买资格) -- 确认库存足够后执行更新 UPDATE table SET balls=balls-5 WHERE marketId=3212 AND balls >=5; COMMIT;
注意事项:
- 确保表使用InnoDB引擎(MyISAM不支持行锁);
- 事务要尽可能短,避免长时间占用锁导致其他请求超时;
- 依赖InnoDB默认隔离级别(REPEATABLE READ)即可,无需额外调整。
3. 乐观锁(版本号机制)
针对极高并发场景,乐观锁是更高效的选择——它不会主动锁定行,而是通过版本号检测并发冲突:
- 给表新增
version字段(INT类型,默认值0); - 读取库存时同时获取当前版本号:
SELECT balls, version FROM table WHERE marketId=3212; - 更新时带上版本号条件,仅当版本号匹配时执行更新:
UPDATE table SET balls=balls-5, version=version+1 WHERE marketId=3212 AND balls >=5 AND version=读取到的版本号; - 检查受影响行数:
- 受影响行数为1:更新成功;
- 受影响行数为0:说明有其他请求已修改该行,可重试(最多几次)或提示用户“操作失败,请重试”。
这种方式无锁等待,性能损耗低,但需要处理重试逻辑。
内容的提问来源于stack exchange,提问作者Stuart William
相关产品推荐
相关产品推荐

