通常,select ... for update是为了锁定相关的行,保证在查询期间到释放的时候,相关的行集在这个过程中不被其他会话进行写操作(但是其他行集却并无影响)。


比如,有表:


create table t ( id number); 
  
 
  

    insert into t select object_id from dba_objects where object_id<=100; 
  
 
  

    commit;

现在t表有100行数据,从1到100;


然后我们在两个会话中进行测试: select ... for update


会话A

会话A执行结果

会话B

会话B执行结果

delete from t where id=2;

执行成功。





select * from t for update;

会话被挂起。

commit;

提交成功。


A会话提交后,B会话执行成功。得到结果集。

update t set id=-1 where  id =10;

会话被挂起。




执行成功。

commit或rollback(这里实际上并无任何数据更改)。

提交成功或回滚成功。


这里,我们看到。当某一行被会话A修改或删除锁定时,如果其他会话执行select for update,就会被挂起。直到会话A提交或回滚,释放了锁。


而当某个会话执行了select ... for update 后,其他会话身体在相关的行集进行写操作(更新或删除),也会被挂起,直到该会话解除锁定。其他会话的写操作才能完成。

那么select ... for update nowait 又有什么区别呢?

接下来测试 : select ... for update nowait


会话A

会话A执行结果

会话B

会话B执行结果

delete from t where id=2;

执行成功。





select * from t for update nowait;

立即返回错误。






第 1 行出现错误:
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源

commit;

提交成功。


A会话提交后,B会话执行成功。得到结果集。

update t set id=-1 where  id =10;

会话被挂起。




执行成功。

commit或rollback(这里实际上并无任何数据更改)。

提交成功或回滚成功。




通 过两个比的对比发现。当使用 select ...  for update 的时候,如果有相关的行被锁定。那么,查询会被挂起,直到其他会话的锁定被解除。使用select ... for update nowait 的时候,如果有相关行被锁定。不再是挂起,是直接返回一个资源忙的错误。

另外,无论是使用select ... fro update 还是使用 select ... fro update nowait。都会对相关的行集进行锁定,其他会话进行相关行集的写操作都会被挂起,直到该锁定被解除。所以,在使用了select ... fro update或者 select ... fro update nowait 后,要尽快解除锁定,避免其他会话挂起太久。


https://blog.51cto.com/bearbear/1329321