一、锁类型

根据保护的对象不同,Oracle数据库锁可以分为以下几大类:

1.1、DML锁

DML锁(data locks,数据锁),用于保护数据的完整性;一般是指select update、insert、merge、delete 语句。 DML锁机制允许并发执行数据修改,例如,DML锁可能是特定数据行上的锁,或者是锁定表中所有行的表级锁。

1.2、DDL锁(dictionary locks,字典锁)

DDL锁(dictionary locks,字典锁)用于保护数据库对象的结构,如表、索引等的结构定义;DDL 表示数据定义语言,如create 和alter 等

1.3、内部锁和闩

(内部锁和闩internal locks and latches ;System Locks 系统锁),保护数据库的内部结构。 例如oracle解析一个查询并生成优化的查询计划时,它会把库缓存闩上,将查询计划放在那里,以供其他会话使用,闩(latch)是oracle采用的一种轻量级的低串行话设备,功能类似于锁。轻量级是指闩的实现,而不是它的影响。闩是数据库中导致竞争的一个常见原因。

oracle官方文档关于锁的说明::https://docs.oracle.com/cd/E11882_01/server.112/e40540/consist.htm#CNCPT1351

2、DML锁

2.1 TX锁

A row lock, also called a TX lock
行锁,也称为事务锁
事务发起第一个修改时会得到TX锁(事务锁)。事务的发起是自动的(在oracle中,不需要明显的开始一个事务)
TX锁会被一直持有。直至事务执行提交或者回滚。TX锁被用做一钟排队机制,使得其他会话可以等待这个事务完成。
事务中修改或通过select for update选择的任何行都会指向该事务的一个相关TX锁。这听上去好像开销很大,但实际
并非如此。在oracle中锁是作为数据的一个属性被保存的。oracle并没有一个传统的锁管理器以及锁管理器为系统
中锁定的每一行维护的一个长长从列表。但是对于其他数据库却是这么做的,因为对这些数据库来说,锁是一种稀有
资源,需要锁对使用进行监控,使用的锁越多,系统管理的方面就会越多,所以对于这些数据库,对
使用大量锁是由顾虑的。

Oracle的锁机制是一种轻量级的锁定机制,不是通过构建锁列表来进行数据的锁定管理,而是直接将锁作为数据块的属性,存储在数据块首部。这个是通过ITL( Interested Transaction List事务槽)来实现的,一个事务要修改块中的数据,必须获得该块中的一个itl(通过initrans预先分配的或者是通过free space构建的)。通过itl和undo segment header中的transaction table,可以知道事务处于活动阶段,还是已经完成。事务在修改块时(其实就是在修改行)会检查行中row header中的标志位,如果该标志位为0(该行没有被活动的事务锁住,这是可能要进行deferred block cleanout等工作),就把该标志位修改为事务在该块获得的itl的序号,这样当前事务就获得了对记录的锁定,然后就可以修改行数据了,这也就是oracle行锁实现的原理。

2.1.1 TX锁定实例

1、v$transaction 每一个活动事务都在中其中有一个条目相对
2、v$session 显示已经登录的会话
3、 v$lock 拥有锁或是正在等待锁的会话所持有的所有队列锁都会在其中有一个条目对应。也就是说
不是一个会话的每个行锁都会在其中有对应的一行。正如之前说oracle不存行级锁的一个主列表,如果
某个会话锁住一个表中的一行,v$lock 视图中有对应这个会话的一行来证明这个一事实,但如果一个会话
锁定了一个表的百万行,v$lock 不会对应百万行,而是对应这个会话还是一行.

我们更新dept_b 表中部门名字 首字母大写

UPDATE dept_b SET dname = initcap(dname);

oracle 锁类型 TX实例演示_oracle


很明显这里开启了一个事务,我们来看看系统此时的状态

–v$lock 使用id1/id2来标识事务。其中:id1的高四字节为回滚段号,低四字节为槽号,id2为序号

SELECT S.USERNAME,
       L.SID,
       TRUNC(l.ID1 / power(2, 16)) rbs,--power(x,y) :计算x^y次方 截断获取高4字节
       bitand(l.ID1, to_number('ffff', 'xxxx')) + 0 slot,---bitand 按位与 按位与获取低4字节
       l.ID2 seq,
       l.LMODE,
       l.REQUEST
  FROM v$session s, v$lock l
 WHERE l.TYPE = 'TX'
   AND l.SID = s.SID
   AND s.USERNAME = 'SCOTT';

oracle 锁类型 TX实例演示_oracle_02


查看事务情况

SELECT t.XIDUSN, t.XIDSLOT, t.XIDSQN FROM v$transaction t;

oracle 锁类型 TX实例演示_数据_03


这里有几点意思

1、v$lock 中的lmod为6,request为0,查看oralce database reference 手册看查看

v$lock 的定义 lmod=6 是一个排他锁,请求request=0 意味着没有发出请求,意味着该事务拥有这个锁

2、v$lock 这个表只有一行,尽管锁定了4行。oracle不会在任何地方存储行级锁的列表
3、 本例选择了id1和id2,并它们进行了处理,oracle需要保存3个16位数,但是对应的只有两列,
这是因为id1列保存着2个数,通过 TRUNC(l.ID1 / power(2, 16)) 截取高4位赋给RBS,
然后通过 bitand获取第四位 赋给slot
4、RBS,SLOT,seq 值与v$transaction 信息匹配。这就是本例事务的ID

然后在另一用户下启动一个会话,更新员工表中的某些行,并视图更新dept_b表

update emp_b set ename=upper(ename);
update dept_b set deptno=deptno-10;

oracle 锁类型 TX实例演示_oracle锁类型_04


很明显更新dept_b 会产生阻塞

我们再来查看v$lock 中的信息

SELECT S.USERNAME,
       L.SID,
       TRUNC(l.ID1 / power(2, 16)) rbs,--power(x,y) :计算x^y次方 截断获取高4字节
       bitand(l.ID1, to_number('ffff', 'xxxx')) + 0 slot,---bitand 按位与 按位与获取低4字节
       l.ID2 seq,
       l.LMODE,
       l.REQUEST
  FROM v$session s, v$lock l
 WHERE l.TYPE = 'TX'
   AND l.SID = s.SID
   AND s.USERNAME = 'SCOTT';

oracle 锁类型 TX实例演示_TX锁定实例演示_05

SELECT t.XIDUSN, t.XIDSLOT, t.XIDSQN FROM v$transaction t;

oracle 锁类型 TX实例演示_数据_06


可以看到开始了一个新的事物, 事物ID 是(9,13,3456).这个新会话(SID=365),

在v$lock 有两行,其中一行表示有锁是lmod=6,另外一行显示了一个值为6的request。这是一个对排他锁的请求,有意思的是这个请求行 rbs/slog/seq 事务的ID 正式锁持有者的事务ID。SID=405的事务

阻塞了SID=365的事务。只需要执行v$lock 的一个自连接查询就可以更明确看到这一点。

SELECT s1.USERNAME, l1.SID, 'is blocking', l2.SID, s2.USERNAME
  FROM v$lock l1, v$lock l2, v$session s1, v$session s2
 WHERE l1.BLOCK = 1
   AND l2.REQUEST > 0
   AND l1.ID1 = l2.ID1
   AND l1.ID2 = l2.ID2
   AND l1.SID = s1.SID
   AND l2.SID = s2.SID;

oracle 锁类型 TX实例演示_数据_07


现在提交用户1 sid=405 中对dept_b 的修改,并重新运行锁查询,可以看到请求行不见了

oracle 锁类型 TX实例演示_数据库_08


oracle 锁类型 TX实例演示_oracle_09

SELECT t.XIDUSN, t.XIDSLOT, t.XIDSQN FROM v$transaction t;

oracle 锁类型 TX实例演示_oracle锁类型_10


另一会话一旦放弃锁,请求行就会消失,这个请求行就是排队机制,一旦事务完成, 数据库就会唤醒

被阻塞的会话。

oracle 锁类型 TX实例演示_数据_11