1. 程式人生 > >SELECT... for update,排他鎖機制的簡單理解

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.