在Oracle数据库的并发业务场景中,多个会话同时对同一批数据进行修改是常见情况,如果直接执行Update语句,可能会因为锁等待或数据覆盖引发业务异常。使用SELECT FOR UPDATE可以在查询目标数据时就对其加行级锁,确保后续Update操作的安全性。

SELECT FOR UPDATE基本语法
SELECT FOR UPDATE的核心作用是对查询出来的行加上行级排他锁,其他会话只能对这些行进行查询,无法进行修改或删除操作,直到当前事务提交或回滚释放锁。
基础语法格式如下:
SELECT 列名1, 列名2, ... FROM 表名 WHERE 筛选条件 FOR UPDATE;
如果要锁定特定行,只需要在WHERE子句中加上对应的筛选条件即可,比如要锁定用户表中用户ID为1001的行,语句如下:
SELECT user_id, user_name, user_status FROM t_user WHERE user_id = 1001 FOR UPDATE;
结合Update操作的完整实现流程
实际业务中通常是先锁定目标行,再执行Update修改,整个流程需要在同一个事务中完成,步骤如下:
- 第一步:开启事务,Oracle中默认是自动提交事务,需要先设置不自动提交,或者显式使用事务控制语句
- 第二步:执行带FOR UPDATE的查询语句,锁定需要修改的特定行
- 第三步:执行Update语句修改锁定的行数据
- 第四步:提交事务释放行锁,或者回滚事务取消修改和锁定
完整的PL/SQL示例代码如下:
-- 关闭自动提交,不同客户端设置方式不同,这里以显式事务为例
SET AUTOCOMMIT OFF;
-- 锁定用户ID为1001的行
SELECT user_name, user_status
FROM t_user
WHERE user_id = 1001
FOR UPDATE;
-- 执行Update操作修改锁定的行
UPDATE t_user
SET user_status = 'ACTIVE',
update_time = SYSDATE
WHERE user_id = 1001;
-- 提交事务,释放行锁
COMMIT;
进阶用法与注意事项
锁定多行的情况
如果WHERE条件匹配多行,SELECT FOR UPDATE会锁定所有匹配的行,比如要锁定状态为INACTIVE的所有用户行:
SELECT user_id, user_status FROM t_user WHERE user_status = 'INACTIVE' FOR UPDATE;
此时所有状态为INACTIVE的行都会被加行锁,其他会话无法修改这些行,直到当前事务结束。
NOWAIT和WAIT选项
默认情况下,如果目标行已经被其他会话锁定,当前会话会一直等待直到锁释放。可以使用NOWAIT选项让当前会话不等待,直接返回错误:
SELECT user_id, user_status FROM t_user WHERE user_id = 1001 FOR UPDATE NOWAIT;
也可以使用WAIT选项指定等待的秒数,比如等待5秒:
SELECT user_id, user_status FROM t_user WHERE user_id = 1001 FOR UPDATE WAIT 5;
注意事项
- SELECT FOR UPDATE的锁会在事务提交或回滚时自动释放,不要长时间持有事务,避免锁等待影响其他业务
- FOR UPDATE只会锁定查询匹配的行,不会锁定整个表,属于行级锁,对业务并发影响较小
- 如果查询语句中使用了ORDER BY等子句,不影响行锁的加锁范围,锁仍然只作用于WHERE条件匹配的行
- 不要在锁定行之后执行和锁定行无关的大量耗时操作,尽量缩短事务持有锁的时间
常见问题说明
有开发者会疑问,直接执行Update语句难道不会加锁吗?实际上Update语句本身也会对修改的行加行锁,但是如果先查询再Update,在两个操作之间可能有其他会话修改了数据,使用SELECT FOR UPDATE可以把查询和加锁合并成一个步骤,避免这个时间差内的数据不一致问题。
另外需要注意,SELECT FOR UPDATE不能和SELECT ... FOR UPDATE OF 列名混淆,OF子句是用来指定锁定哪个表的行,在多表关联查询时使用,单表查询时不需要加OF子句。
OracleSELECT_FOR_UPDATE行锁定Update操作修改时间:2026-06-21 00:27:26