必须用begin transaction+trycatch包裹,update带where qty>=@delta校验,commit前检查@@rowcount和余额≥0,禁用裸更新;参数优先用table变量,分批处理并按主键升序防死锁。

必须用事务包裹 + 显式校验 + 分批控制,否则库存字段极易出现负数、超卖或覆盖丢失。
SQL Server 存储过程里怎么安全更新库存字段
核心是把“查余额 → 判是否足够 → 扣减”三步压进一个事务,并强制用主键驱动。常见错误是先 SELECT 再 UPDATE,中间被并发修改导致超卖。
- 必须用
BEGIN TRANSACTION+TRY...CATCH包裹整段逻辑,COMMIT前检查@@ROWCOUNT和更新后余额是否 ≥ 0 - 禁止写成
UPDATE stock SET qty = qty - @delta WHERE id = @id这种裸更新——它不防负数,也不校验前置条件 - 正确写法是带校验的原子更新:
UPDATE stock SET qty = qty - @delta WHERE id = @id AND qty >= @delta,然后用IF @@ROWCOUNT = 0 THROW 50001, '库存不足', 1 - 传入参数推荐用
TABLE类型变量(如@updates StockUpdateType READONLY),别用逗号拼接字符串
MySQL 存储过程中如何避免库存更新死锁
死锁不是因为语句错,而是多线程按不同顺序更新同一组商品 ID。哪怕只更新两行,只要 A 会话先更新 ID=100 再 ID=200,B 会话反过来,InnoDB 就可能升级间隙锁冲突。
- 所有批量更新必须加
ORDER BY id强制访问顺序一致,例如在临时表里先INSERT ... SELECT ... ORDER BY id - 禁用
UPDATE t SET x = x + 1 WHERE id IN (SELECT id FROM temp)这类子查询写法(MySQL 5.7 下易全表锁),改用JOIN:UPDATE t JOIN temp b ON t.id = b.id SET t.x = t.x + 1 - 设置
innodb_lock_wait_timeout为 10–30 秒,并在应用层捕获Deadlock found when trying to get lock后重试一次 - 慎用
SELECT ... FOR UPDATE,尤其不要在循环里反复执行;优先用带条件的UPDATE替代
PostgreSQL 函数中处理库存唯一约束与并发冲突
PostgreSQL 不支持 TRYCATCH,但能用 EXCEPTION 捕获具体错误码。库存场景最常遇到的是扣减后触发唯一索引校验失败(比如限制最小库存为 0),或并发下 INSERT ... ON CONFLICT 的竞态。
- 唯一约束冲突对应
SQLSTATE '23505'(unique_violation),不能只写WHEN OTHERS,否则会吞掉磁盘满等真正严重错误 - “存在则更新,不存在则插入”类逻辑,优先用
INSERT ... ON CONFLICT DO UPDATE(9.5+),比EXCEPTION块更轻量、更可靠 - 如果必须用函数封装复杂判断(如多级库存池分配),
EXCEPTION块内要手动RAISE EXCEPTION并拼接上下文,用GET STACKED DIAGNOSTICS取原始错误信息 - 函数必须声明为
SECURITY DEFINER且用plpgsql,普通DO块无法显式控制事务
真正难的不是写对一条 UPDATE,而是确保它在高并发、分库分表、跨服务调用时依然不丢不重不乱。库存字段一旦出错,后续补偿成本远高于预防成本——所以事务边界、主键排序、错误码精确捕获,这三点漏掉任何一环都等于埋雷。











