直接用 UPDATE ... WHERE stock >= 1 即可防超卖,因InnoDB自动对匹配行加行级X锁且判断更新原子执行;返回ROW_COUNT()为1表示成功,0表示失败。

直接用 UPDATE ... WHERE stock >= 1 就能防超卖,不需要额外加锁或事务包装
绝大多数超卖问题,不是因为没加锁,而是把“查库存”和“扣库存”拆成了两步。只要把检查和更新压进一条语句,InnoDB 自动对匹配行加行级排他锁(X Lock),且整个判断+更新是原子的。返回 ROW_COUNT() 为 1 表示成功,0 表示库存不足或已被抢光——无需应用层判断中间状态。
常见错误现象:写成先 SELECT stock FROM goods WHERE id = 1,再 if (stock > 0) UPDATE ...。两个并发请求都读到 stock=1,都进入 if 分支,最终都执行成功,库存变 -1。
- 这条
UPDATE语句本身已隐含事务边界:InnoDB 对它加 X 锁、做原子写、失败时自动回滚已改部分 - 不用显式
BEGIN/COMMIT,除非你还想在同个事务里干别的事(比如生成订单) - 必须确保
id是主键或有唯一索引,否则可能锁表或锁错范围
SELECT ... FOR UPDATE 只在需要“查后做复杂业务逻辑”时才用,且必须配完整事务
如果你的流程不只是扣减库存,还要查价格、校验用户资格、预占优惠券等,那确实得先查数据再决策——这时才轮到 FOR UPDATE 出场。但它极易踩坑,不是加了就安全。
典型失效场景:autocommit=1 状态下执行 SELECT ... FOR UPDATE,语句一结束锁就释放;或者查询条件没走索引,导致锁升级为表锁甚至被死锁检测干掉。
- 必须显式开启事务:
BEGIN或START TRANSACTION - 查询语句必须带锁:
SELECT stock, price, version FROM goods WHERE id = 1 FOR UPDATE,不能只用普通SELECT - 所有后续操作(包括
UPDATE、INSERT)必须在同一个事务内,且最后COMMIT或ROLLBACK - 客户端连接不能处于
autocommit=1模式,否则每条语句自成事务,锁毫无意义
乐观锁适合高并发读多写少场景,但别只靠 version 字段忽略库存约束
乐观锁本质是“不锁,但更新时校验”,适用于秒杀类流量大、冲突少的场景。但它不是万能的,尤其当库存字段本身就能当判断依据时,硬加 version 反而多一层维护成本。
容易被忽略的点:用 UPDATE ... SET stock = stock - 1 WHERE id = 1 AND version = ?,却没同时校验 stock > 0。结果版本号对了,但库存早已是负数——这说明乐观锁只是防“覆盖写”,不防业务逻辑错。
- 推荐组合写法:
UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = 1 AND stock >= 1 AND version = ? - 必须检查
ROW_COUNT():0 表示失败,需重试或拒绝;不能只看 SQL 是否执行成功 - 重试逻辑要控制次数和间隔,避免雪崩式重试打爆数据库
- 如果业务允许“预占库存”,建议拆出
frozen_stock字段,用两条原子UPDATE分别处理冻结与确认
行锁失效的三个关键信号:锁表、死锁日志、SHOW ENGINE INNODB STATUS 显示等待
你以为加了 FOR UPDATE 就锁住了某一行,结果还是超卖?大概率锁根本没生效。InnoDB 行锁是有前提的,不是写了 WHERE 就自动走行锁。
最典型的线索是慢查询日志里出现大量 Lock wait timeout exceeded,或者 SHOW ENGINE INNODB STATUS 输出中看到 TRANSACTIONS 部分有大量 waiting for lock,甚至明确写出 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: 后面跟着一堆记录。
- 查询条件未命中索引:比如
WHERE status = 1但status列没索引 → InnoDB 可能全表扫描并锁所有行 - 使用了非唯一索引查多行:
WHERE category_id = 5→ 锁住该索引下所有匹配项,可能远超预期 - WHERE 条件含函数或表达式:
WHERE ABS(id) = 1或WHERE created_at > NOW() - INTERVAL 1 DAY→ 索引失效,退化为全表扫描
真正可靠的防超卖,从来不是“我用了锁”,而是“我验证过这条 SQL 在并发下只影响一行、且返回值能准确反映业务成败”。任何依赖中间状态判断的逻辑,哪怕加了锁,也埋着雷。



















