锁和闩

锁(lock)用于管理对共享资源的并发访问。

在必要时才提交,不要提前。

 

锁的问题:

1.丢失更新

session1 session2
(1)查询一行数据 (2)查询同一行数据
(3)update该行并提交  
  (4)update该行并提交
这个过程被称为丢失更新 因为(3)所做的所有修改都会丢失

 

2.锁定策略:悲观锁(pessimistic)乐观锁(optimistic)

悲观锁
这种方式在用户修改数值之前就开始生效了
乐观锁
所有锁定动作都延迟到即将执行更新之前才进行
     实现方法:使用版本列的乐观锁,使用用户总和校验的乐观锁

3.阻塞

阻塞的Insert
     带主键或唯一约束
     引用完整性约束
阻塞的Merger、update和delete

4.死锁

5.锁升级

 

锁的类型

DML锁
      select、insert、update、merge、delete
      可能是行级锁也可能是表级锁
   1.TX锁
      事务发起第一个修改时会得到TX锁
      Oracle没有使用锁管理器,Oracle锁定过程如下:
         (1)找到想锁定的那一行地址
         (2)到达那一行
         (3)就地锁住这一行,就在行的位置上(如果这一行已经被锁住,就等待锁住他的事务结束,除非指定nowait)

   2.TM(DML Enqueue)锁

      TM锁用于确保在修改表内容时,表结构不会被改变。例如,你已经更新了一个表中的行,那同时也会得到这个表上的一个TM锁。这会防止另一个用户在该表上执行DROP或ALTER命令。如果你有一个TM锁,另一个用户试图在这个表上执行DDL,他就会得到以下错误消息:

SQL> update EMPLOYEE set comm=200 where EMPNO=7844;

1 row updated.

 
  SQL> drop table EMPLOYEE;
drop table EMPLOYEE
           *
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

在11gR2以及更高版本中,可以设置DDL_lock_timeout让DDL等待

SQL> alter session set DDL_lock_timeout=10;

Session altered.

DDL锁
      create和alter语句等,可以保护对象结构定义

      有以下3种类型的DDL锁

      排他DDL锁(exclusive DDL lock)

            防止其他会话得到他们自己的DDL锁或TM(DML)锁。这说明,在DDL期间可以查询一个表,但无法以任何方式修改这个表。

            例如:alter tale t move;

      共享DDL锁(share DDL lock)

            这些锁会保护所引用的对象的结构,使之不会被其他会话修改,但是允许修改数据。

            例如:create index t_idx on t(x) online;

      可中断解析锁(breakable parse lock)

内部锁0159BD8B和闩latch
      保护内部数据结构

 

TX锁实验:

SQL> create table dept
  2  as select * from scott.dept;

Table created.

SQL> create table emp
  2  as select * from scott.emp;

Table created.
SQL> alter table dept
  2  add constraint dept_pk
  3  primary key(deptno);

Table altered.
SQL> alter table emp
  2  add constraint emp_pk
  3  primary key(empno);

Table altered.

SQL> alter table emp
  2  add constraint emp_fk_dept
  3  foreign key(deptno)
  4  references dept(deptno);

Table altered.

SQL> create index emp_deptno_idx
  2  on emp(deptno);

Index created.

启动一个事务:

SQL> update dept
  2  set dname=initcap(dname);

4 rows updated.

查看:

SELECT username,
       v$lock.sid,
       TRUNC (id1 / POWER (2, 16)) rbs,
       BITAND (id1, TO_NUMBER ('ffff', 'xxxx')) + 0 slot,
       id2 seq,
       lmode,
       request
  FROM v$lock, v$session
WHERE     v$lock.TYPE = 'TX'
       AND v$lock.sid = v$session.sid
       AND v$session.username = USER;

USERNAME                  SID        RBS       SLOT        SEQ    LMODE     REQUEST
--------------------------- ---------- ---------- ---------- ---------- ---------- ----------
EODA                               341       7         17        158        6           0

SQL> select XIDUSN,XIDSLOT,XIDSQN from v$transaction;

XIDUSN  XIDSLOT XIDSQN
---------- ---------- ----------
         7       17         158
这里有几点很有意思

v$lock表的LMODE为6,REQUEST为0.查看Oracle Database Reference得到LMODE=6是一个排他锁。请求REQUEST=0则意味着你没有发出请求,也就是说你拥有这个锁。

这个v$lock表只有一行。Oracle不会在任何地方存储行级锁的列表。要查看某一行是否锁定,必须直接找到这一行。

RBS、SLOT和SEQ值与v$transaction匹配,这就是我的事务ID

开启另外一个会话

SQL> update emp set ename=upper(ename);

14 rows updated.

SQL> update dept set deptno=deptno-10;


现在这个会话被阻塞
回到原来会话

SELECT username,
       v$lock.sid,
       TRUNC (id1 / POWER (2, 16)) rbs,
       BITAND (id1, TO_NUMBER ('ffff', 'xxxx')) + 0 slot,
       id2 seq,
       lmode,
       request
  FROM v$lock, v$session
WHERE     v$lock.TYPE = 'TX'
       AND v$lock.sid = v$session.sid
11         AND v$session.username = USER;

USERNAME         SID        RBS       SLOT      SEQ      LMODE     REQUEST
------------------- ---------- ---------- ---------- ---------- ---------- ----------
EODA                      311      10          3       7727        6           0
EODA                      311       7         17        158        0           6
EODA                      341       7         17        158        6           0

SQL> select XIDUSN,XIDSLOT,XIDSQN from v$transaction;

    XIDUSN    XIDSLOT      XIDSQN
---------- ---------- ----------
    10        3        7727
     7       17         158
可以看到开始了一个新事务,事务ID是(10,3,7727),这一次,这个新会话(SID=311)在v$lock有两行。其中一行表示他拥有的锁(LMODE=6)。另外还有一行,显示了一个值为6的REQUEST。这是一个队排他锁的请求。有意思的是,这个请求行的RBS/SLOT/SEQ的值正是锁持有者的事务ID。SID=341的事务阻塞了sid=311的事务。从v$lock的自连查询可以看出:

SELECT (SELECT username
          FROM v$session
         WHERE sid = a.sid)
          blocker,
       a.sid,
       ' is blocking ',
       (SELECT username
          FROM v$session
         WHERE sid = b.sid)
          blockee,
       b.sid
  FROM v$lock a, v$lock b
13   WHERE a.BLOCK = 1 AND b.REQUEST > 0 AND a.ID1 = b.ID1 AND a.id2 = b.id2;

BLOCKER                   SID 'ISBLOCKING'    BLOCKEE                   SID
------------------------------ ---------- ------------- ------------------------------ ----------
EODA                      341  is blocking    EODA                      311
现在,提交原来的事务(SID=341),再次查询,可以看到请求行不见了

SQL> commit;

Commit complete.

SELECT username,
       v$lock.sid,
       TRUNC (id1 / POWER (2, 16)) rbs,
       BITAND (id1, TO_NUMBER ('ffff', 'xxxx')) + 0 slot,
       id2 seq,
       lmode,
       request
  FROM v$lock, v$session
WHERE     v$lock.TYPE = 'TX'
       AND v$lock.sid = v$session.sid
11         AND v$session.username = USER;

USERNAME                  SID     RBS       SLOT        SEQ    LMODE     REQUEST
------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
EODA                      311      10          3       7727        6           0

再另外一个会话也可以看到更新完成,结束了被阻塞状态。

SQL> update emp set ename=upper(ename);

14 rows updated.

SQL> update dept set deptno=deptno-10;

4 rows updated.

TM锁实验:

oracle@test1: /home/oracle> sqlplus eoda/foo

SQL*Plus: Release 11.2.0.4.0 Production on Mon Dec 19 17:25:16 2016

Copyright (c) 1982, 2013, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create table t1(x int);

Table created.

SQL> create table t2(x int);

Table created.

SQL> insert into t1 values(1);

1 row created.

SQL> insert into t2 values(1);

1 row created.

SELECT (SELECT USERNAME
          FROM v$session
         WHERE sid = v$lock.sid)
          username,
       sid,
       id1,
       id2,
       lmode,
       request,
       block,
       v$lock.TYPE
  FROM v$lock
 13   WHERE sid = SYS_CONTEXT ('userenv', 'sid');

USERNAME               SID           ID1     ID2    LMODE    REQUEST       BLOCK TY
----------------------------- ---------- ---------- ---------- ---------- ---------- ---------- --
EODA                      331          100        0      4        0           0 AE
EODA                      331       19824        0      3        0           0 TM
EODA                      331       19825        0      3        0           0 TM
EODA                      331     655373    9271      6        0           0 TX


SQL> col OBJECT_NAME format a20
select object_name ,object_id
from user_objects
  3  where object_id in(19824,19825);

OBJECT_NAME          OBJECT_ID
-------------------- ----------
T1              19824
T2              19825

尽管每个事物只得到一个TX锁,但TM锁不同,修改了多少个对象,就能得到多少个TM锁。

 

posted @ 2016-12-15 18:58  guilingyang  阅读(701)  评论(0编辑  收藏  举报