SELECT... for update,排他锁机制的简单理解

会话1中:
创建一个表
SQL> create table t0416a (id number,val number);  

Table created.

插入一行记录并提交
SQL> insert into t0416a values (1,10);

1 row created.

SQL> commit;

Commit complete.

查看表中的记录,这时VAL列的值为10.
SQL> select * from t0416a;

        ID          VAL
---------- ----------
         1           10

假设操作人员认为应该对VAL列的值加5,使其等于15。则发出以下操作,但未提交。
SQL> update t0416a set val=val+5 where id=1;

1 row updated.

这时再打开另一个会话,称之为会话2.模拟另一位操作人员。
查看该表中的值,显示VAL的值仍为10。这是正确的。因为会话1中的修改未提交。
SQL> select * from t0416a;

        ID          VAL
---------- ----------
         1           10

操作人员2也发现这个值不对,应该对VAL列的值加5,使其等于15.操作人员2在会话2中也发起对这个值的修改。
SQL> update t0416a set val=val+5 where id=1;

但由于会话1中对该行记录的修改未提交,所以,排它锁并没有释放,因而操作人员2发起的这个修改操作会等待,直至会话1释放该锁(提交或回滚)。

假设这时操作人员1在会话1中提交了。(注意,此时会话2中的修改获得了锁,修改也生效了,但未提交。)
SQL> commit;

Commit complete.

操作人员1这时做检查,发现VAL值如其期望的一样,是15.
SQL> select * from t0416a;

        ID          VAL
---------- ----------
         1           15
         
再回到会话2.这时操作人员2发现自己的语句已经执行了,但如果他这时检查一下,会发现VAL列的值并不是其期望的15,而20了。
SQL> select * from t0416a;

        ID          VAL
---------- ----------
         1           20
         
但很多时候,我们是不会去做这个确认,通常一看语句成功执行,会一如往常提交之。
SQL> commit;

Commit complete.

但一旦这样做了,数据已经被改成了并不是我们所期望的值。这时,无论是在哪个会话中,查看这个VAL值,均会是20.

而造成这个问题的原因,是因为oracle的一个重要特性:一致性读。正是这个特性,使得ORACLE读不阻塞写,写也不会阻塞读,这个特点使得ORACLE的并发性能很好。
但如上面的实验所示,这样一来,当多人在同时修改(在其它人提交修改前,也发出了修改命令),而且还是对同一条记录的同一个列做修改时,就会发生错误。
这时,我们就可以用for update来避免这个问题。
即,在我们试图修改某个记录时,先利用select ... from xxx where zzz for update;将该记录加锁。这样,就可以确保我现在看到的值,一定不会再被其他人修改了。
用上面演示的例子来说,这时我看到的VAL值是10,那么就一定是10。如果这时其上有其它人未提交的修改,那么我前面发出的select ... from xxx where zzz for update;
语句是处于等待状态,不会返回结果。只要它能返回结果,就一定是已经在这条记录上加锁了,从此刻起,其它人是不可能对其进行修改的。那么我对其修改后,其结果
一定是期望的15,而不会出现上例中出现的20.

猜你喜欢

转载自blog.csdn.net/qq_35779969/article/details/80074770