通常,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 后,要尽快解除锁定,避免其他会话挂起太久。