Select For update語句淺析 (轉)


 

 

Select … for update語句是我們經常使用手工加鎖語句。通常情況下,select語句是不會對數據加鎖,妨礙影響其他的DMLDDL操作。同時,在多版本一致讀機制的支持下,select語句也不會被其他類型語句所阻礙。

 

借助for update子句,我們可以在應用程序的層面手工實現數據加鎖保護操作。本篇我們就來介紹一下這個子句的用法和功能。

 

下面是采自Oracle官方文檔《SQL Language Reference》中關於for update子句的說明:(請雙擊點開圖片查看)

 

 

 

for update子句的語法狀態圖中,我們可以看出該子句分為兩個部分:加鎖范圍子句和加鎖行為子句。下面我們分別針對兩個方面的進行介紹。

 

加鎖范圍子句

 

select…for update之后,可以使用of子句選擇對select的特定數據表進行加鎖操作。默認情況下,不使用of子句表示在select所有的數據表中加鎖。

 

//采用默認格式for update

SQL> select * from emp where rownum<2 for update;

 

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

 7369 SMITH      CLERK      7902 1980-12-17     800.00               20

 

 

此時,我們觀察v$lockv$locked_object視圖,可以看到鎖信息。

//事務信息視圖

SQL> select addr,xidusn,xidslot,xidsqn from v$transaction;

 

ADDR         XIDUSN    XIDSLOT     XIDSQN

-------- ---------- ---------- ----------

377DB5D0          7         19        808

//鎖對象信息

SQL> select xidusn,xidslot,xidsqn,object_id,session_id, oracle_username from v$locked_object;

 

    XIDUSN    XIDSLOT     XIDSQN  OBJECT_ID SESSION_ID ORACLE_USERNAME

---------- ---------- ---------- ---------- ---------- ------------------------------

         7         19        808      73181         36 SCOTT

//

SQL> select owner,object_name from dba_objects where object_id=73181;

 

OWNER                          OBJECT_NAME

------------------------------ ------------------------------------------------------------

SCOTT                          EMP

 

//

SQL> select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

 

ADDR      SID TYPE        ID1        ID2      LMODE    REQUEST  BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0    36 AE          100          0          4          0    0

B7DE8A44   36 TM        73181          0          3          0   0

377DB5D0   36 TX       458771        808          6          0    0

 

 

從上面的情況看,默認情況下的for update語句,效果相當於啟動了一個會話級別的事務,在對應的數據表(select所涉及的所有數據表)上加入一個數據表級共享鎖(TMlmode=3)。同時,在對應的數據行中加入獨占鎖(TXlmode=6)。

 

根據我們以前的知識,如果此時有另一個會話視圖獲取對應數據行的獨占權限(無論是用update/delete還是另一個for update),都會以block而告終。

 

SQL> select sid from v$mystat where rownum<2;

 

       SID

----------

        37

 

SQL> select * from emp where empno=7369 for update;

 

//系統blocking

 

此時系統中狀態,切換到另一個用戶下進行觀察:

 

SQL> select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid in (36,37);

 

ADDR   SID TYPE        ID1        ID2      LMODE    REQUEST      BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0   0

37E80ED4         37 AE          100          0          4          0   0

37E80F48         37 TX       458771        808          0          6   0

B7DE8A44         37 TM        73181          0          3          0  0

B7DE8A44         36 TM        73181          0          3          0  0

377DB5D0         36 TX       458771        808          6          0  1

 

6 rows selected

 

SQL> select * from dba_waiters;

 

WAITING_SESSION HOLDING_SESSION LOCK_TYPE                  MODE_HELD                                MODE_REQUESTED                             LOCK_ID1  LOCK_ID2

--------------- --------------- -------------------------- ---------------------------------------- ---------------------------------------- ---------- ----------

             37              36 Transaction                Exclusive                                Exclusive                                    458771        808

 

 

由此,我們可以獲取到結論:for update子句的默認行為就是自動啟動一個事務,借助事務的鎖機制將數據進行鎖定。

 

 

Of子句是配合for update語句使用的一個范圍說明標記。從官方的語法結構看,后面可以跟一個或者多個數據列列表。這種語法場景常常使用在進行連接查詢的select中,對其中一張數據表數據進行鎖定。

 

SQL> select empno,ename,job,mgr,sal from emp,dept where emp.deptno=dept.deptno and empno=7369 for update of emp.empno;

 

EMPNO ENAME      JOB         MGR       SAL

----- ---------- --------- ----- ---------

 7369 SMITH      CLERK      7902    800.00

 

SQL>  select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

 

ADDR       SID TYPE        ID1        ID2      LMODE    REQUEST BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0    0

B7E1C2E8         36 TM        73181          0          3         0    0

377DBC0C         36 TX        65566        747          6       0   0

 

 

上面的語句中,我們看到使用for update of指定數據列之后,鎖定的范圍限制在了所在的數據表。也就是說,當我們使用連接查詢配合of子句的時候,可以實現有針對性的鎖定。

 

同樣在連接查詢的時候,如果沒有of子句,同樣采用默認的模式,會如何呢?

 

SQL> select empno,ename,job,mgr,sal from emp,dept where emp.deptno=dept.deptno and empno=7369 for update;

 

EMPNO ENAME      JOB         MGR       SAL

----- ---------- --------- ----- ---------

 7369 SMITH      CLERK      7902    800.00

 

SQL>  select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

 

ADDR     SID TYPE        ID1        ID2      LMODE    REQUEST  BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0     0

B7E1C2E8         36 TM        73179          0          3          0   0

B7E1C2E8         36 TM        73181          0          3          0     0

377DBC0C         36 TX       458777        805          6          0    0

 

SQL> select owner,object_name from dba_objects where object_id=73179;

 

OWNER                          OBJECT_NAME

------------------------------ --------------------------------------------------------------------------------

SCOTT                          DEPT

 

 

明顯可以看到,當我們沒有使用of子句的時候,默認就是對所有select的數據表進行lock操作。

 

 

加鎖行為子句

 

加鎖行為子句相對比較容易理解。這里分別介紹。

 

Nowait子句

 

當我們進行for update的操作時,與普通select存在很大不同。一般select是不需要考慮數據是否被鎖定,最多根據多版本一致讀的特性讀取之前的版本。加入for update之后,Oracle就要求啟動一個新事務,嘗試對數據進行加鎖。如果當前已經被加鎖,默認的行為必然是block等待。

 

使用nowait子句的作用就是避免進行等待,當發現請求加鎖資源被鎖定未釋放的時候,直接報錯返回。

///session1

SQL> select * from emp for update;

 

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

 7369 SMITH      CLERK      7902 1980-12-17     800.00               20

 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30

 7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30

 7566 JONES      MANAGER    7839 1981-4-2      2975.00               20

 

//變換session,進行執行。

SQL> select * from emp for update nowait;

 

select * from emp for update nowait

 

ORA-00054: 資源正忙但指定以 NOWAIT 方式獲取資源或者超時失效

 

 

對應的還有就是wait子句,也就是默認的for update行為。一旦發現對應資源被鎖定,就等待blocking,直到資源被釋放或者用戶強制終止命令。

 

 

wait子句還存在一個數據參數位,表示當出現blocking等待的時候最多等待多長時間。單位是秒級別。

//接上面的案例

SQL> select * from emp for update wait 3;

 

select * from emp for update wait 3

 

ORA-30006: 資源已被占用執行操作時出現 WAIT 超時

 

 

 

Skip locked參數

 

Skip locked參數是最新引入到for update語句中的一個參數。簡單的說,就是在對數據行進行加鎖操作時,如果發現數據行被鎖定,就跳過處理。這樣for update就只針對未加鎖的數據行進行處理加鎖。

 

//session1中,對一部分數據加鎖;

SQL> select * from emp where rownum<4 for update;

 

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

 7369 SMITH      CLERK      7902 1980-12-17     800.00               20

 7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30

 7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30

 

//session2中;

SQL> select * from emp for update skip locked;

 

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

(篇幅原因,省略)

7934 MILLER     CLERK      7782 1982-1-23     1300.00               10

 

11 rows selected

 

 

總數據一共14行。Session1中,先lock住了3行數據。之后的seesion2中,由於使用的skip locked子句參數,將剩下的11條數據進行讀取到並且加鎖。

 

 

for update的使用

 

在日常中,我們對for update的使用還是比較普遍的,特別是在如pl/sql developer中手工修改數據。此時只是覺得方便,而對for update真正的含義缺乏理解。

 

For updateOracle提供的手工提高鎖級別和范圍的特例語句。Oracle的鎖機制是目前各類型數據庫鎖機制中比較優秀的。所以,Oracle認為一般不需要用戶和應用直接進行鎖的控制和提升。甚至認為死鎖這類鎖相關問題的出現場景,大都與手工提升鎖有關。所以,Oracle並不推薦使用for update作為日常開發使用。而且,在平時開發和運維中,使用了for update卻忘記提交,會引起很多鎖表故障。

 

那么,什么時候需要使用for update?就是那些需要業務層面數據獨占時,可以考慮使用for update。場景上,比如火車票訂票,在屏幕上顯示郵票,而真正進行出票時,需要重新確定一下這個數據沒有被其他客戶端修改。所以,在這個確認過程中,可以使用for update。這是統一的解決方案方案問題,需要前期有所准備。

 

http://blog.itpub.net/17203031/viewspace-694383/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM