SelectForupdate语句浅析
Select …语句是我们经常使⽤⼿⼯加锁语句。通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作。同时,在多版本⼀致读机制的⽀持下,select语句也不会被其他语句所阻碍。
借助for update⼦句,我们可以在应⽤程序的层⾯⼿⼯实现数据加锁保护操作。本篇我们就来介绍⼀下这个⼦句的⽤法和功能。
下⾯是采⾃官⽅⽂档《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 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 pno;
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
select语句查询日期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⽅式获取资源,或者超时失效
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论