Select For update语句浅析
Select …forupdate語句是我們經常使用手工加鎖語句。通常情況下,select語句是不會對數據加鎖,妨礙影響其他的DML和DDL操作。同時,在多版本一致讀機制的支持下,select語句也不會被其他類型語句所阻礙。
?
借助for update子句,我們可以在應用程序的層面手工實現數據加鎖保護操作。本篇我們就來介紹一下這個子句的用法和功能。
?
下面是采自Oracle官方文檔《SQLLanguage 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?????79021980-12-17????800.00??????????????20
?
?
此時,我們觀察v$lock和v$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所涉及的所有數據表)上加入一個數據表級共享鎖(TM,lmode=3)。同時,在對應的數據行中加入獨占鎖(TX,lmode=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?????????????36Transaction???????????????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?????79021980-12-17????800.00??????????????20
?7499 ALLEN?????SALESMAN??76981981-2-20????1600.00???300.00????30
?7521 WARD??????SALESMAN??76981981-2-22????1250.00???500.00????30
?7566 JONES?????MANAGER???78391981-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?????79021980-12-17????800.00??????????????20
?7499 ALLEN?????SALESMAN??76981981-2-20????1600.00???300.00????30
?7521 WARD??????SALESMAN??76981981-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?????77821982-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 update是Oracle提供的手工提高鎖級別和范圍的特例語句。Oracle的鎖機制是目前各類型數據庫鎖機制中比較優秀的。所以,Oracle認為一般不需要用戶和應用直接進行鎖的控制和提升。甚至認為死鎖這類鎖相關問題的出現場景,大都與手工提升鎖有關。所以,Oracle并不推薦使用for update作為日常開發使用。而且,在平時開發和運維中,使用了for update卻忘記提交,會引起很多鎖表故障。
?
那么,什么時候需要使用for update?就是那些需要業務層面數據獨占時,可以考慮使用for update。場景上,比如火車票訂票,在屏幕上顯示郵票,而真正進行出票時,需要重新確定一下這個數據沒有被其他客戶端修改。所以,在這個確認過程中,可以使用for update。這是統一的解決方案方案問題,需要前期有所準備
總結
以上是生活随笔為你收集整理的Select For update语句浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ORACLE11G自动维护任务简析
- 下一篇: example:利用impdp通过dbl