mysql并发扣减库存如何实现_mysql原子操作设计

发布时间 - 2026-01-04 00:00:00    点击率:
确做法是UPDATE时在WHERE中加入库存校验条件,如WHERE id = 123 AND stock >= 1,并检查ROW_COUNT();复杂逻辑需配合SELECT FOR UPDATE加行锁,且WHERE条件避免函数导致索引失效。

UPDATE WHERE 语句必须带库存校验

并发扣减库存最直接的错误,就是只写 UPDATE product SET stock = stock - 1 WHERE id = 123。这会导致超卖:两个请求同时读到 stock=1,各自执行减 1,最终变成 -1。

正确做法是把库存是否充足判断直接塞进 WHERE 条件里,让 MySQL 在更新前原子性校验:

UPDATE product SET stock = stock - 1 WHERE id = 123 AND stock >= 1;

执行后检查 ROW_COUNT()(MySQL 返回影响行数):

  • 返回 1 → 扣减成功
  • 返回 0 → 库存不足或记录不存在,需业务层拒绝下单

务必使用 SELECT FOR UPDATE 配合事务

当扣减逻辑不止一行 SQL(比如要先查价格、再扣库存、再写订单),单纯靠 WHERE 校验不够,必须加行锁防止并发读写冲突。

关键点:

  • 必须在 START TRANSACTION 内执行
  • SELECT ... FOR UPDATE 会锁定该行(即使没命中索引,可能升级为表锁)
  • 锁持续到事务结束(COMMITROLLBACK),不是语句结束

示例:

START TRANSACTION;
SELECT stock FROM product WHERE id = 123 FOR UPDATE;
-- 此时其他事务对 id=123 的 SELECT FOR UPDATE / UPDATE 会被阻塞
UPDATE product SET stock = stock - 1 WHERE id = 123 AND stock >= 1;
-- 检查 ROW_COUNT(),失败则 ROLLBACK
COMMIT;

避免 WHERE 条件中使用函数或表达式导致索引失效

如果写成 WHERE id = ? AND stock - 1 >= 0,MySQL 无法用上 stock 索引(哪怕有联合索引),可能触发全表扫描+锁表,极大降低并发能力。

应始终保持 WHERE 中的列是独立出现的:

  • WHERE id = 123 AND stock >= 1(能走 PRIMARY KEY + 范围条件)
  • WHERE id = 123 AND stock - 1 >= 0stock - 1 是表达式,索引失效)
  • WHERE id = 123 AND stock > 0(虽然语义等价,但某些旧版本优化器可能不走索引,>= 1 更稳)

高并发场景下慎用自增 ID 做库存分片依据

有人想用 id % 4 把商品分散到不同行来缓解热点,但这会破坏业务语义(同一商品多行库存难聚合),且无法解决单商品高并发问题。

真正有效的分片思路是:

  • 按业务维度拆分:比如「SKU + 仓库编码」作为联合主键,不同仓库存独立扣减
  • 用 Redis 预减库存(异步落库),但需处理 Redis 故障/回滚一致性问题
  • 数据库端改用乐观锁(version 字段)+ 重试,适合冲突率低的场景

纯 MySQL 方案里,UPDATE ... WHERE ... AND stock >= N + 事务 + 索引覆盖,已经能扛住几千 QPS 的秒杀流量;再往上,就得考虑缓存、队列、分库分表了。


# mysql  # redis  # 编码  # 热点  # red  # sql  # for  # select  # 并发  # 异步  # 数据库  # 这会  # 分片  # 不存在  # 就得  # 要先  # 不走  # 想用  # 升级为  # 读到  # 塞进 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】  专业商城网站制作公司有哪些,pi商城官网是哪个?  如何在IIS管理器中快速创建并配置网站?  Laravel队列由Redis驱动怎么配置_Laravel Redis队列使用教程  如何用AI帮你把自己的生活经历写成一个有趣的故事?  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧  如何批量查询域名的建站时间记录?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  如何在香港免费服务器上快速搭建网站?  如何快速使用云服务器搭建个人网站?  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  Laravel如何使用Livewire构建动态组件?(入门代码)  Laravel storage目录权限问题_Laravel文件写入权限设置  Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程  android nfc常用标签读取总结  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  Laravel如何实现多对多模型关联?(Eloquent教程)  如何快速生成ASP一键建站模板并优化安全性?  JS弹性运动实现方法分析  专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?  Laravel全局作用域是什么_Laravel Eloquent Global Scopes应用指南  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  ,网页ppt怎么弄成自己的ppt?  Angular 表单中正确绑定输入值以确保提交与验证正常工作  动图在线制作网站有哪些,滑动动图图集怎么做?  如何快速启动建站代理加盟业务?  网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  zabbix利用python脚本发送报警邮件的方法  如何快速查询网址的建站时间与历史轨迹?  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  如何选择PHP开源工具快速搭建网站?  谷歌浏览器如何更改浏览器主题 Google Chrome主题设置教程  深入理解Android中的xmlns:tools属性  ,怎么在广州志愿者网站注册?  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  Laravel如何实现API速率限制?(Rate Limiting教程)  Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中  如何在服务器上三步完成建站并提升流量?  Laravel如何构建RESTful API_Laravel标准化API接口开发指南  Linux系统命令中tree命令详解  Bootstrap CSS布局之列表  夸克浏览器网页跳转延迟怎么办 夸克浏览器跳转优化  如何用西部建站助手快速创建专业网站?  Laravel怎么使用Intervention Image库处理图片上传和缩放  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案