ITPub博客

首页 > 数据库 > Oracle > select ... ... for update

select ... ... for update

原创 Oracle 作者:singlve 时间:2014-10-13 17:18:06 0 删除 编辑
    for update 是使用手工加锁的语句。默认情况下的for update语句,效果相当于启动了一个会话级别的事务,在对应的数据表(select所涉及
的所有数据表)上加入一个数据表级共享锁(TM,lmode=3),同时,在对应的数据行中加入独占锁(TX,lmode=6)。

我们以scott的emp表来对上述内容进行验证:
在session1中:
SQL> select empno,ename,job,sal,deptno from scott.emp where sal<1500 for update;

     EMPNO ENAME      JOB              SAL     DEPTNO
---------- ---------- --------- ---------- ----------
      7369 SMITH      CLERK            800         20
      7521 WARD       SALESMAN        1250         30
      7654 MARTIN     SALESMAN        1250         30
      7876 ADAMS      CLERK           1100         20
      7900 JAMES      CLERK            950         30
      7934 MILLER     CLERK           1300         10


已选择6行。


查询锁对象信息
SQL> select xidusn,xidsqn,object_id,session_id,oracle_username from v$locked_object;


    XIDUSN     XIDSQN  OBJECT_ID SESSION_ID ORACLE_USERNAME
---------- ---------- ---------- ---------- ------------------------------
          4        664      73196        141 SYS
          
SQL> select owner ,object_name from dba_objects where object_id=73196;


OWNER      OBJECT_NAM
---------- ----------
SCOTT      EMP


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

ADDR            SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
-------- ---------- -- ---------- ---------- ---------- ---------- ----------
3D378898        141 AE        100          0          4          0          0
1062F30C        141 TM      73196          0          3          0          0
3CFC0E80        141 TX     262166        664          6          0          1


也可以用如下语句查询锁的情况
SELECT 
LPAD (' ', DECODE (l.xidusn, 0, 3, 0))
|| l.oracle_username
User_name,
o.owner,
o.object_name,
o.object_type,
s.sid,
s.serial#
FROM v$locked_object l, dba_objects o, v$session s
WHERE l.object_id = o.object_id AND l.session_id = s.sid
ORDER BY o.object_id, xidusn DESC


我们在另开一个session2:
SQL> drop table scott.emp;
drop table scott.emp
                 *
第 1 行出现错误:
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效


SQL> select empno,ename,job,sal,deptno from scott.emp where sal<1500;


     EMPNO ENAME      JOB              SAL     DEPTNO
---------- ---------- --------- ---------- ----------
      7369 SMITH      CLERK            800         20
      7521 WARD       SALESMAN        1250         30
      7654 MARTIN     SALESMAN        1250         30
      7876 ADAMS      CLERK           1100         20
      7900 JAMES      CLERK            950         30
      7934 MILLER     CLERK           1300         10


已选择6行。


这时我们看到,我们想删除scott的emp表时报资源忙,但却可以对emp进行查询,说明针对emp表的for update
对表加了共享锁。


继续在session2中,我们尝试对emp中数据进行更改:
我们先更改在上面查询结果中没有出现过的记录,发现没问题;
SQL> update scott.emp set sal=8888 where empno=7902;


已更新 1 行。


但当我们更新for update查询出来的记录时发现事物会被挂起。
WAITING_SESSION HOLDING_SESSION LOCK_TYPE    MODE_HELD    MODE_REQUEST  LOCK_ID1  LOCK_ID2
--------------- --------------- ------------ ------------ ------------  --------  ---------
             17             141 Transaction  Exclusive    Exclusive     262166        664
这时,我们在session1中提交或者回滚事物,session2的更改才能成功。
这说明,for update不仅在表上加了共享锁,在指定的数据行上也加了独占锁。


select ... ... for update of 是配合for update使用的一个范围说明标记,只对of后面的数据列所在的表数据进行锁定,一般用在多表连接查询时。


SQL> select a.empno,a.ename,a.sal,b.dname from scott.emp a,scott.dept b where a.
deptno=b.deptno and a.empno=7902 for update of a.empno;


     EMPNO ENAME             SAL DNAME
---------- ---------- ---------- --------------
      7902 FORD             3000 RESEARCH
      
SQL> select addr,sid,type,id1,id2,lmode,request,block from v$lock where sid=141;




ADDR            SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
-------- ---------- -- ---------- ---------- ---------- ---------- ----------
3D378898        141 AE        100          0          4          0          0
3F2BF30C        141 TM      73196          0          3          0          0
3CFC0E80        141 TX     393245        916          6          0          0


SQL> select a.empno,a.ename,a.sal,b.dname from scott.emp a,scott.dept b where a.
deptno=b.deptno and a.empno=7902 for update of a.empno,b.dname;


     EMPNO ENAME             SAL DNAME
---------- ---------- ---------- --------------
      7902 FORD             3000 RESEARCH
      
SQL> select addr,sid,type,id1,id2,lmode,request,block from v$lock where sid=141;




ADDR            SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
-------- ---------- -- ---------- ---------- ---------- ---------- ----------
3D378898        141 AE        100          0          4          0          0
3F2BF30C        141 TM      73194          0          3          0          0
3F2BF30C        141 TM      73196          0          3          0          0
3CFC0E80        141 TX     524316        900          6          0          0


SQL> select a.empno,a.ename,a.sal,b.dname from scott.emp a,scott.dept b where a.
deptno=b.deptno and a.empno=7902 for update;


     EMPNO ENAME             SAL DNAME
---------- ---------- ---------- --------------
      7902 FORD             3000 RESEARCH
      
SQL> select addr,sid,type,id1,id2,lmode,request,block from v$lock where sid=141;




ADDR            SID TY        ID1        ID2      LMODE    REQUEST      BLOCK
-------- ---------- -- ---------- ---------- ---------- ---------- ----------
3D378898        141 AE        100          0          4          0          0
3F2BF30C        141 TM      73194          0          3          0          0
3F2BF30C        141 TM      73196          0          3          0          0
3CFC0E80        141 TX     524316        900          6          0          0


从上可以看出,锁定只会发生在of后面数据列所在的表,多表查询时,不写of字句,会锁定所有的数据表




select ... ... for update nowait 
使用nowait子句的作用就是避免进行等待,当发现请求加锁资源被锁定未释放的时候,直接报错返回。


SQL> select empno,ename,job,sal from scott.emp where empno=7902 for update;


     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7902 FORD       ANALYST         3000
      
SQL> select empno,ename,job,sal from scott.emp where empno=7902 for update nowait;
select empno,ename,job,sal from scott.emp where empno=7902 for update nowait
                                      *
第 1 行出现错误:
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效


select ... ... for update wait n  //n指的秒级时间
当等待锁资源超多n秒时就报错


SQL> select empno,ename,job,sal from scott.emp where empno=7902 for update wait 5;
select empno,ename,job,sal from scott.emp where empno=7902 for update wait 5
                                      *
第 1 行出现错误:
ORA-30006: 资源已被占用; 执行操作时出现 WAIT 超时


select ... ... for update skip locked
Skip locked参数是在对数据行进行加锁操作时,如果发现数据行被锁定,就跳过处理。这样for update就只针对未加锁的数据行进行处理加锁。


session1
SQL> select empno,ename,job,sal from scott.emp where sal<1500 for update;


     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7369 SMITH      CLERK            800
      7521 WARD       SALESMAN        1250
      7654 MARTIN     SALESMAN        1250
      7876 ADAMS      CLERK           1100
      7900 JAMES      CLERK            950
      7934 MILLER     CLERK           1300


已选择6行。


session2 
SQL>  select empno,ename,job,sal from scott.emp  for update skip locked;


     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7499 ALLEN      SALESMAN        1600
      7566 JONES      MANAGER         2975
      7698 BLAKE      MANAGER         2850
      7782 CLARK      MANAGER         2450
      7788 SCOTT      ANALYST         3000
      7839 KING       PRESIDENT       5000
      7844 TURNER     SALESMAN        1500
      7902 FORD       ANALYST         3000


已选择8行。
此时scott的emp表所有记录都被加锁,针对emp表的所有锁资源请求都将挂起。


for update虽然能够很方便地对表进行手工加锁操作,但是建议在日常开发和运维中尽量少用,如果使用了for update却忘记提交,会引起很多锁表故障。
不过对于那些需要业务层面数据独占时,可以考虑使用for update,比如说订票系统。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29150106/viewspace-1297453/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2014-04-24

  • 博文量
    2
  • 访问量
    22449