select for update仅锁定查询结果集中的行,非整表或幻影行;锁持续至事务提交或回滚;需显式开启事务、where条件一致且含主键,否则锁无效。

SELECT FOR UPDATE 会锁住哪些行
执行 SELECT ... FOR UPDATE 后,Oracle 只锁定查询结果集中的行,不是整张表,也不是所有满足 WHERE 条件但未被查出的行(比如被其他事务已删或未提交的幻影行)。锁在事务提交或回滚后才释放,不是语句执行完就释放。
常见错误现象:ORA-00054: resource busy and acquire with NOWAIT specified,说明目标行正被另一个事务用 FOR UPDATE 锁住,且你加了 NOWAIT;若没加 NOWAIT,则会一直等待,直到超时或对方释放锁。
- 必须在同一个事务中后续执行
UPDATE,否则锁无效——因为 SELECT 结束后若没开启事务,Oracle 会自动提交(在某些客户端如 SQL*Plus 默认 autocommit=on) - 如果 WHERE 条件用了非唯一索引字段,可能触发锁升级(如锁住整个索引范围),尤其在 RC 隔离级别下需警惕
- 使用
FOR UPDATE SKIP LOCKED可跳过已被锁的行,适合并发消费队列类场景
UPDATE 前必须显式开启事务
很多开发者以为执行完 SELECT ... FOR UPDATE 就能直接 UPDATE,结果发现更新了别的行、或者没锁住——根本原因是没控制事务边界。Oracle 默认不开启事务,SELECT FOR UPDATE 只在当前事务内有效。
实操建议:
- 在 SQL*Plus 或 SQL Developer 中,先执行
SET AUTOCOMMIT OFF;否则每条语句都是独立事务,SELECT FOR UPDATE执行完立刻释放锁 - 在 JDBC 中,调用
connection.setAutoCommit(false),再executeQuery("SELECT ... FOR UPDATE"),最后executeUpdate("UPDATE ..."),统一commit() - 不要依赖应用层“紧接着执行 UPDATE”来保证原子性——网络延迟、线程调度都可能导致中间插入其他操作
WHERE 条件必须和 SELECT FOR UPDATE 一致
很多人写完 SELECT id, name FROM orders WHERE status = 'PENDING' FOR UPDATE,然后执行 UPDATE orders SET status = 'PROCESSED' WHERE id = ?,却发现锁失效或更新了不该更新的行。问题在于:UPDATE 的 WHERE 没复用原查询的约束逻辑,数据库无法确认你操作的是同一组行。
正确做法是确保 UPDATE 精确作用于刚查出的行:
- SELECT 中必须包含足够定位唯一行的列(通常是主键),例如
SELECT id, status FROM orders WHERE status = 'PENDING' AND ROWNUM - UPDATE 应基于 SELECT 返回的主键值,如
UPDATE orders SET status = 'PROCESSED' WHERE id = :selected_id - 避免在 UPDATE 中改用其他条件(如再次写
WHERE status = 'PENDING'),这会导致语义漂移:可能更新到另一事务刚改成 PENDING 的新行,而非你 SELECT 锁定的那行
FOR UPDATE 和 UPDATE … WHERE CURRENT OF 的区别
WHERE CURRENT OF 是游标级语法,比手动传主键更安全,它直接绑定到刚打开的 FOR UPDATE 游标位置,避免主键值在 SELECT 和 UPDATE 之间被篡改或丢失。
适用场景:PL/SQL 存储过程中批量处理、或需要严格行粒度控制的逻辑。
DECLARE CURSOR c IS SELECT id, amount FROM accounts WHERE balance <p>注意:<code>CURRENT OF</code> 只能在 PL/SQL 中用,不能用于 JDBC 或普通 SQL 脚本;且游标必须声明为 <code>FOR UPDATE</code>,FETCH 后才能用。</p> <p>容易被忽略的一点:如果 SELECT FOR UPDATE 没有返回任何行,游标为空,此时 <code>WHERE CURRENT OF</code> 会报 <code>ORA-01002: fetch out of sequence</code>,需检查 FETCH 是否成功再执行 UPDATE。</p>











