SELECT FOR UPDATE 是数据库事务中实现行级排他锁的关键语句,用于防止并发修改导致数据不一致,需在事务内使用,锁住索引匹配的行,避免全表扫描和过大锁范围,并支持 NOWAIT、SKIP LOCKED 等进阶控制。
SELECT FOR UPDATE 是在数据库事务中实现行级锁的关键语句,主要用于防止并发修改导致的数据不一致。它会在查询到的行上加写锁(排他锁),其他事务无法对这些行执行 UPDATE、DELETE 或再次 SELECT FOR UPDATE,直到当前事务提交或回滚。
适用于“读取-修改-写入”典型场景,比如:
注意:仅在事务内(BEGIN / START TRANSACTION)使用才有效;自动提交模式下会立即提交,锁也随即释放,起不到保护作用。
语法结构简单,但细节
决定成败:
WHERE status = 'pending')会锁定所有匹配行innodb_lock_wait_timeout 调整假设有一张商品表 products(id, name, stock),要安全扣减 ID=100 的商品库存 1 件:
START TRANSACTION;-- 加锁查询,确保后续更新基于最新且被锁定的值 SELECT stock FROM products WHERE id = 100 FOR UPDATE;
-- 应用层判断:if stock >= 1 → 执行更新 UPDATE products SET stock = stock - 1 WHERE id = 100;
COMMIT;
如果另一事务同时执行相同 SELECT FOR UPDATE,它会阻塞等待,直到前一个事务结束。若超时则报错 Lock wait timeout exceeded,应用需捕获并重试或提示失败。
避免长时间阻塞,可主动控制行为:
SELECT ... FOR UPDATE NOWAIT(Oracle/PostgreSQL 支持):不等待,直接报错SELECT ... FOR UPDATE SKIP LOCKED(MySQL 8.0+/PostgreSQL):跳过已被锁的行,常用于任务队列分发SELECT ... FOR UPDATE LIMIT 1):只锁满足条件的第一行,适合选一个可用资源例如任务队列中取一个待处理任务,多个工作进程并发执行时,用 SKIP LOCKED 可避免争抢同一行:
SELECT * FROM tasks WHERE status = 'pending' ORDER BY created_at LIMIT 1 FOR UPDATE SKIP LOCKED;