一、什么是锁:
Oracle的锁机制是一种轻量级的锁定机制,不是通过构建锁列表来进行数据的锁定管理,而是直接将锁作为数据块的属性,存储在数据块首部。这个是通过ITL来实现的,一个事务要修改块中的数据,必须获得该块中的一个itl。关于itl(事务槽)的介绍可参考http://blog.csdn.net/gybyylx/article/details/6893639。
说明:在oracle数据库中,不存在真正意义上属于某个对象或数据的锁。oracle锁的信息是数据块的一个物理属性,而不是逻辑上属于某个表或某个行。
二、为什么需要锁:
oracle为什么好好的要搞一个锁(lock)机制出来呢?很简单,那是因为需要它。想想看我们自己家的门为什么要锁起来呢?这个我们肯定都知道怕家里的东西丢失或被别人拿走,如果没有别的人家世界就你一家那就没有必要锁门。oracle里锁也是一个道理,如果是单用户的系统,那完全没有必要这个锁,就是因为有多用户并发操作,我们为了确保资源的安全性(也就是oracle的数据完整性和一致性)才引申出这个锁出来。Oracle 利用其锁机制来实现事务间的数据并发访问及数据一致性。
三、锁的模式:
oracle有两种模式的锁:排他锁(exclusive lock,即X锁)和共享锁(share lock,即S锁)。
当数据对象被加上排它锁时,其他的事务不能对它读取(可以读取undo)和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
四、锁的类型:
根据保护的对象不同,Oracle数据库锁可以分为以下几大类:
DML锁(data locks,数据锁):用于保护数据的完整性。
DDL锁(dictionary locks,字典锁):用于保护数据库对象的结构,如表、索引等的结构定义。
内部锁和闩(internal locks and latches):保护数据库的内部结构,例如,数据文件。内部锁及闩锁的管理完全由Oracle 自动完成。
1.DML锁:
DML锁的目的在于保证并发情况下的数据完整性,在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。DML 语句能够自动地获得所需的表级锁(TM)与行级(事务)锁(TX)。
a.表级锁(TM)
TM 锁用于确保在修改表的内容时,表的结构不会改变,例如防止在 DML 语句执行期间相关的表被移除。当用户对表执行 DDL 或 DML 操作时,将获取一个此表的表级锁。
Oracle的TM锁类型
锁模式 锁描述 解释 sql操作
0 none
1 NULL 空 Select
2 SS(Row-S) 行级共享锁,其他对象只能查询这些数据行 Select for update、Lock for update、Lock row share
3 SX(Row-X) 行级排它锁,在提交前不允许做DML操作 Insert、Update、Delete、Lock row share
4 S(Share) 共享锁 Create index、Lock share
5 SSX(S/Row-X) 共享行级排它锁 Lock share row exclusive
6 X(Exclusive) 排它锁 Alter table、Drop able、Drop index、Truncate table 、Lock exclusive
b.事务(行级)锁(TX)
事务发起第一个修改时会得到TX 锁(事务锁),而且会一直持有这个锁,直至事务执行提交(COMMIT)或回滚(ROLLBACK)。TX 锁用作一种排队机制,使得其他会话可以等待这个事务执行。事务中修改或通过SELECT FOR UPDATE 选择的每一行都会“指向”该事务的一个相关TX 锁。一个事务要修改块中的数据,必须获得该块中的一个itl,通过itl和undo segment header中的transaction table,可以知道事务是否处于活动阶段。事务在修改块时(其实就是在修改行)会检查行中row header中的标志位,如果该标志位为0(该行没有被活动的事务锁住),就把该标志位修改为事务在该块获得的itl的序号,这样当前事务就获得了对记录的锁定,然后就可以修改行数据了,这也就是oracle行锁实现的原理。
2.DDL锁:
在DDL 操作中会自动为对象加DDL 锁(DDL Lock),从而保护这些对象不会被其他会话所修改。例如,如果我执行一个DDL 操作ALTER TABLE T,表T 上就会加一个排他DDL 锁,以防止其他会话得到这个表的DDL锁和TM 锁。在DDL 语句执行期间会一直持有DDL 锁,一旦操作执行就立即释放DDL 锁。实际上,通常会把DDL 语句包装在隐式提交(或提交/回滚对)中来执行这些工作。由于这个原因,在Oracle 中DDL 一定会提交。每条CREATE、ALTER 等语句实际上都如下执行(这里用伪代码来展示):
Begin
Commit;
DDL-STATEMENT
Commit;
Exception
When others then rollback;
End;
因此,DDL 总会提交(即使提交不成功也会如此)。DDL 一开始就提交,一定要知道这一点。它首先提交,因此如果必须回滚,它不会回滚你的事务。如果你执行了DDL,它会使你所执行的所有未执行的工作成为永久性的,即使DDL 不成功也会如此。如果你需要执行DDL,但是不想让它提交你现有的事务,就可以使用一个自治事(autonomous transaction)。DDL锁分以下几种:
排他DDL 锁(Exclusive DDL lock):这会防止其他会话得到它们自己的DDL 锁或TM(DML)锁。这说明,在DDL 操作期间你可以查询一个表,但是无法以任何方式修改这个表。
共享DDL 锁(Share DDL lock):这些锁会保护所引用对象的结构,使之不会被其他会话修改,但是允许修改数据。
可中断解析锁(Breakable parse locks):这些锁允许一个对象(如共享池中缓存的一个查询计划)向另外某个对象注册其依赖性。如果在被依赖的对象上执行DDL,Oracle 会查看已经对该对象注册了依赖性的对象列表,并使这些对象无效。因此,这些锁是“可中断的”,它们不能防止DDL 出现。
3.闩(latch):是轻量级的串行化设备,用于协调对共享数据结构、对象和文件的多用户访问。关于latch后续单独介绍。
4.手动锁定:
1. 通过一条SQL 语句(例如select for update)手动地锁定数据。
2. 通过DBMS_LOCK 包创建我们自己的锁。
悲观封锁和乐观封锁
a、悲观封锁
悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住:
Select ..for update(nowait)
Select * from tab1 for update
用户发出这条命令之后,oracle将会对返回集中的数据建立行级封锁,以防止其他用户的修改。
如果此时其他用户对上面返回结果集的数据进行dml或ddl操作都会返回一个错误信息或发生阻塞。
1:对返回结果集进行update或delete操作会发生阻塞。
2:对该表进行ddl操作将会报:Ora-00054:resource busy and acquire with nowait specified.
原因分析
此时Oracle已经对返回的结果集上加了排它的行级锁,所有其他对这些数据进行的修改或删除操作都必须等待这个锁的释放,产生的外在现象就是其他的操作将发生阻塞,这个这个操作commit或rollback.
同样这个查询的事务将会对该表加表级锁,不允许对该表的任何ddl操作,否则将会报出ora-00054错误::resource busy and acquire with nowait specified.
b、乐观封锁
乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。这里面有一种潜在的危险就是由于被选出的结果集并没有被锁定,是存在一种可能被其他用户更改的可能(丢失更新问题)。乐观锁和悲观锁各有优缺点,视具体实际情况来定。
关于oracle悲观锁和乐观锁的介绍可参考:http://blog.csdn.net/gybyylx/article/details/4474270
本文只是对oracle锁做了一个大概介绍,后续会对oracle锁机制做进一步的探讨。