Oracle系列之六 锁和闩

一、概述

锁是协调对共享资源访问的一种机制。此处共享资源可能是数据行、表或者别的什么很多人都需要访问的资源。

Oracle数据库实现锁的机制跟别的数据库不同,在Oracle数据库中锁并不算的上是稀缺的资源,当然不合适的持有很多锁一定会降低程序的并发行和扩展性的。

二、锁定问题

考虑下这样一种场景,表T中有一行数据记录了一个人的信息,A和B两个客户端都要去修改,A和B都先获取了这个人的信息,然后A会修改地址,B会修改了证件号码。修改完后A先提交了修改,把用户的信息更新了一遍,提交事务后,B也进行了修改,提交。此时再去查看此人信息,发现A做的修改已经被覆盖,即相当于A没有进行任务操作。

解决问题的办法有两种:悲观锁定乐观锁定

悲观锁定:当一个客户端要修改一条记录的时候, 就把该记录锁定,直到修改完提交了事务,如通过select * from T for update。这种办法当然有很一些问题了,在并发情况下对记录的锁定时间很长,如果是一些比较核心的资源,这种方式会极大降低并发度。在另外一些更新数量的场景下,情况变得更糟糕。

另外,如果是b/s应用模式,这问题就变得复杂了,每次请求一个事务,打开用户信息的时候,事务就已经完了,这种行上的锁也被释放了,等真正更新的时候已经没有了锁。

乐观锁定:我们在修改记录的时候,不像悲观锁定一样,一开始便锁定。而是在开始的时候记录一个关键的信息,等 真正要修改时候根据这个信息来判断我们在开始决定修改的时候到更新时候为止,有没有被别人更新过,如果更新过了,那么我们就提示修改失败,需要重新来修改。此处的关键信息可以是版本列,如时间戳;也可以是校验和,如该行一些关键字段或者所有字段值的hash/md5值,也可以作为一个虚拟列‘存放’hash/md5值。

但是另外考虑一种情况,在修改包含大量信息的表单的时候,用户辛辛苦苦录了半个小时的单据,在提交的时候说表单已经被修改了,你猜猜那会是什么样的情形?所以作为对悲观锁的一种扩展,我们可以在应用层级实现一种锁定,应用层负责添加、释放锁,并负责客户端处理异常终止、会话超时等情形下锁的释放,同时提供应用级锁定的管理功能,可以管理员手动释放锁。

三、锁

1)、DML锁

顾名思义就是执行dml语句时候加的锁,如update、delete、select、insert等。怎么分成tm和tx锁的?在oracle数据库中select语句是从来不加锁的,对于一个大表,你在一个会话中查询还没有完成的时候,可以在另外一个会话中把表给drop掉,第一个会话中的查询会一直进行到查完所有数据。

  a、TX锁

事务锁,属于行级锁,如果当前没有事务,则第一条修改表数据的sql语句将隐式的开启一个新事务,容易update、 delete、 insert语句,同时该事务会持有一个tx锁,这些语句修改的数据也将指向这把tx锁。如果已经有了事务,则直接指向已有的tx锁。

分别用sys和scott连接数据库,然后用sys查看scott持有的锁:

SQL> select sess.username,sess.sid,lck.type,lck.lmode 
	from v$session sess 
	left join v$lock lck on lck.sid = sess.sid 
where sess.username = upper('scott');

USERNAME          SID TYPE      LMODE
--------------------- ---------- ---- ----------
SCOTT                 132 AE            4

然后用scott更新emp的一条记录后再查询锁的信息:

 

SQL> select sess.username,sess.sid,lck.type,lck.lmode
  2     from v$session sess
  3     left join v$lock lck on lck.sid = sess.sid
  4  where sess.username = upper('scott');

USERNAME                    SID TYPE      LMODE
---------------------------- ---------- ---- ----------
SCOTT                          132 AE            4
SCOTT                          132 TM            3
SCOTT                          132 TX            6
   此处多出了一把TX锁。如果继续用scott更新别的表,如dept表,会看到tm锁增加了一把,但是tx锁仍然仍然是一把。

  b、TM锁

表级锁,Enqueue锁,保护表结构不被别的会话修改。当对表数据进行增删改的时候,就会给表加一个TM锁。上边代码中看到,在修改表数据的时候会给表加一把tm锁。

如果一个表加了tm锁,则不能再加ddl排他锁和ddl共享锁了。

如果向表中插入数据后,事务尚未提交,在新会话中再创建索引则会失败,提示(什么锁跟什么锁的问题?):

ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效。

但是如果在在运行创建索引语句但是并未创建完成的时候,另外一个会话中插入数据,会出现数据插入,索引创建会话等待插入数据会话提交事务的现象,为何?

2)、DDL锁

顾名思义是执行ddl语句时候加的锁,如create table、alter table、drop table、truncate table、create index、create view等。

  a、DDL互斥锁(DDL exclusive lock)

表级锁,当对一个表加了ddl互斥锁的时候,别的会话就不能再加ddl锁和dml锁了,修改表结构会被阻塞,查表中的数据也会被阻塞。如alter table,create table,drop table,truncate table等。

  b、DDl共享锁(DDL share lock)

表级锁,当一个表加了ddl共享锁的时候,别的会话只能获得ddl共享锁,不能加dml锁,不能修改表结构,但是可以查询修改表中的数据。如create view,create index等。

  c、可中断解析所(breakable parse lock)

共享池(只有共享池中么?)中一些对象,如执行计划,游标,统计信息等,会对依赖的一些对象,如表,索引等加可中断解析锁。如果被依赖对象发生了变化,如执行了ddl,oracle会查看已经对该对象注册了依赖性的对象列表,使其失效。

ddl锁可以通过dba_ddl_locks视图获取,这是一个建立在x$表上的视图。

一个会话中执行如下sql但并未执行完时:

create index idx_dpt_bk_deptno on scott.dept_bak(deptno);
  另外一会话中执行查询,最后一行就是创建索引的ddl锁。
SQL> select * from dba_ddl_locks;

SESSION_ID OWNER    NAME            TYPE          MODE_HELD    MODE_REQUESTED
---------- ----- ------------------ ------------ ------------- -------------
    190 SYS       "SYS"."ALERT_QUE":"HAE_SUB"    28         Null      None
    190 SYS       "SYS"."ALERT_QUE":"HAE_SUB"    28         Null      None
    190 SYS       STANDARD           Table/Procedure/Type      Null      None
    190 SYS       DBMS_PRVT_TRACE          Table/Procedure/Type      Null      None
    191 SYS       IS_VPD_ENABLED        Table/Procedure/Type      Null      None
    190 SYS       "SYS"."ALERT_QUE":"YYMT_1830_O    28         Null      None
    190 SYS       AQ$_ALERT_QT_E        10         Null      None
    191 SYS       DBMS_APPLICATION_INFO       Body          Null      None
    190 SYS       DBMS_HA_ALERTS_PRVT         Table/Procedure/Type      Null      None
    191 SYS       SQL_TXT            Table/Procedure/Type      Null      None
    191 SCOTT     IDX_DPT_BK_DEPTNO        Index         Exclusive    None

四、闩

闩是一种低级串行化设备,用来协调对内存结构如数据库块缓冲区缓存和共享池中的库缓存、对象、文件等资源的并发访问。

闩设计为只持有很短的一小段时间,获取闩的时候,如果获取不到则休眠一小段时间后继续获取,不会排队等待。而enqueue锁是队列锁,如果获取队列锁获取不到则会排队。

在获取不到闩的时候,就会出现“自旋”,即在循环中不断重复的去获取闩。因为进程上下文切换的的代价很高,而闩本身被设计为持有很小一段时间的设备,所以进程在被踢出cpu之前会不断重复的试图获取栓,如果循环了一定次数后仍然获取不到,就会被踢出cpu。这个循环的次数可以通过参数设置,默认为2000.可以通过statspack或v$latch视图来查看视图获取栓的信息。

可以通过硬解析和软解析的例子来进一步了解一些闩的细节

五、手动锁定和用户定义锁

1)手动锁定:通过select ···· for update 或lock table手工锁定

2)用户自定义锁:通过dbms_lock包可以创建用户过户自定义锁。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值