首页 > 代码库 > 锁和闩

锁和闩

锁(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)
DDL锁
      create和alter语句等,可以保护对象结构定义
内部锁技术分享和闩latch
      保护内部数据结构

 

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.

锁和闩