Oracle之锁和闩
在开发多用户、数据库驱动应用的时候,最大的难点之一应该时间争取完成最大限度的并发访问,与此同时还要确保每一个用户能以一致的方式读取和修改数据,为此就有了锁定机制。
1. 什么是锁
锁机制用于管理对共享资源的并发访问,Oracle会在行级对表数据锁定,也会在其他多个级别上使用锁,从而对多种不同资源提供并发访问。
2.锁定问题
在讨论Oracle的各类型锁之前,我们先来讨论一下,如果没有正确使用锁定机制可能会产生什么问题。
2.1丢失更新
简单来说就是出现一次啊情况时,就会发生丢失更新:
- A在事物中查询了一行数据
- B在事物中查询了同一行数据
- A修改了这一行,并将事物提交
- B修改 了这一行,并将事物提交
在上述案例中,A所做的修改被,B覆盖了,导致没有实际生效,这种情况就称为丢失更新。在实际生成环境中就可能因为A想修改邮箱,B想修改电话号码,A,B同时去更新同一行,导致A想修改的邮箱,在提交前被B先获取到的旧数据覆盖。 对于这个问题有两种锁定策略,悲观锁定和乐观锁定
2.2悲观锁定
悲观锁定即悲观的认为自己在获取数据库某一行后,一定会有人对该行进行修改,所以在查询完或会立刻加上行锁,例如: 用户一但点击更新某一行,那么在他选择好要更新的行后,就会给该行添加上一个行锁,这个行锁会持续到他修改完数据并且提交后才会释放。 在加上锁定后就可以非常安全的修改这一行,就不会覆盖其他人所做的修改。
2.3乐观锁定
第二种方法称为乐观锁定,就是在获取数据的时候很乐观的认为数据不会被其他用户修改,会在执行更新的时候才去判断数据是否被修改过。 这种锁定方法,用户执行更新失败的可能性就很大,在执行更新的时候发现数据被修改过了就需要重头再来。 实现乐观锁的方式有很多种,即应用会存储行所有的前映像,比如: 使用一个特殊的列,列由一个数据库触发器或者应用程序代码维护,可以告诉我们该行的版本从而确定是否被修改过。 或者使用一个校验或者散列值,使用原来的数据计算得出的。
2.4悲观锁还是乐观锁
这两种方法选择哪种好呢。悲观锁在Oracle中工作的很好,但是需要与数据库有一条状态的连接,不能跨连接,所有如果有大量用户的情况下,悲观锁不太现实。而乐观锁就不存在这个问题。在使用乐观锁就可能会出现用户修改完想执行更新的时候发现数据被其他用户修改了,这时候用户就需要重新获取数据进行修改。对用户体验不好。所有在用户小的时候可以使用悲观锁,用户多的时候还是使用乐观锁。
2.5阻塞
如果一个用户会话持有某个资源的锁,那么另一个会话请求的时候就会出现阻塞,直到锁被释放。 数据库中有5条常见的DML语句可以阻塞:INSERT,UPDATE,DELETE,MERGE,SELECT FOR UPDATE.对于其中的SELECT FOR UPDATE解决方法很简单,只需要加上NOWAIT,就不会阻塞了,应用会向用户报告,这一行已被锁定。而对于其他的四条DML呢。 1、阻塞的INSERT INSERT会阻塞的情况不多,最常见的是,你有一个带唯一约束的表,两个会话试图用同样的值插入一行,这样的话其中一行会话会阻塞,并且在等锁是否后会抛出一个存在重复值的错误。 对于这个最简单解决的方法就是,让数据库本身来生成主键/唯一列值,这样就会避免插入相同的一行。 如果坚持想要用户生成这唯一列值,那么可以使用下面这个解决方法:我们使用一个触发器,他会防止两个或多个会话同时插入相同的值,触发器使用DBMS_UTLITY.GET_HASH_VALUE来计算主键的散列值,得到一个数,然后DBMS_LOCK.REQUEST根据这个ID创建一个排他锁,这样想同时插入一个数据的时候,其他人的请求就会返回资源忙的错误 2、阻塞的Merge、Update、Delete 如果Update或者Delete阻塞,就说明可能存在丢失更新的问题,那么就可以使用Select FOR UPDATE NOTWAIT查询来避免这个问题 由于MARGE只是INSERT和UPDATE,所有可以同时使用这两种技术。
2.6死锁
如果两个会话,每个会话都持有另一会话想要的资源,那么此时就会出现死锁。例如会话A更新表A,会话B更新表B,这时候如何会话A也更新表B,就会阻塞应用会话B已经锁定了表B,这不是死锁只是阻塞而已,而如果会话B也去更新表A这时候就导致了死锁。
3.锁类型
Oracle中主要有3类锁
- DML锁:DML一般来讲表示:SELECT、INSERT、UPDATE、MERGE、DELETE语句。DML锁机制允许并发执行数据修改。
- DDL锁:DDL代表数据定义语言,比如CREATE和ALTER等语句,DDL锁可以保护对象结构定义。
- 内部锁和闩:Oracle使用这些锁来保护其内部数据结构。
3.1DML锁
DML锁确保一次只有一个人能修改某一行。而且你正在处理一个表时,其他人不能删除这个表。 1、TX锁 事物发起第一个修改的时候就会获取一个TX锁(事物锁),直到事物提交或者回滚。TX锁用作一种排队机制,是的其他事物会等待这个事物执行,在事物中修改或者通过select for update都会指向该事物的一个相关的TX锁,并且ORACLE使用闩这个数据的一个属性来进行加锁而不是使用传统的锁管理器。在Oracle加锁的时候只需要找到想锁定的那一行,然后锁定这一行。不需要去专门的列表中去查询这一行是否被锁定。 有意思的是,当你找到数据的是他可能看上去石碑锁定的但是实际上并没有被锁定。Oracle对事物进行锁定的时候,行会指向事物的ID,当另一个会话到来的时候,他就会看多锁住该行的ID,并判断该事物是否是活动的,如果锁不活的则会允许访问这个事物,如果锁是活动的就会要求一但释放就得到通知。 2、TM(DML Enqueue)锁 TM锁用于确保在修改表的内容时,表的结构不会改变,如果已经更新了一个表,就会得到这个表的一个TM锁,会防止另一个用户在该表上执行DROP或者ALTER命令。
3.2DDL锁
在DDL操作中会自动为对象加上DDL锁,一但操作执行就会立刻是否DDL锁。 有以下三种类型的DDL锁:
- 排他DDL锁:这会防止其他会话得到他们自己的DDL锁或TM锁,这说明在DDL操作期间可以查询一个对象,但是无法修改这个表
- 共享DDL锁:这些锁会保护引用对象的结构不会被其他会话修改,但是可以修改数据
- 可中断解析锁:这些锁允许一个对象向另外某个对象注册其依赖性,如果在被依赖的对象上执行DDL,Oracle会查看已经对该对象注册依赖性的对象列表,并是这些对象无效。因此这些锁是可以中断的,不能防止DDL出现。
大多数DDL都带有一个排他的DDL锁。 另一类DDL会获得共享DDL锁,在创建存储的编译对象(如过程或者视图)时,会对依赖的对象家这种共享的DDL锁。例如:
Create view MyView
as
select emp.empno,emp,ename,dept.deptno,dept.dname
form emp,dpt
where emp.deptno=dept.deptno
在create view执行的时候,表emp和dep都会加上共享DDL锁。 最后一类可中断解析锁,当你的会话解析一条语句的时候,对于该语句引用的每一个对象都会加上解析锁,目的是为了,如果以某种方式删除或者修改一个被引用的对象,可以将共享池中已解析的缓存语句置为无效。
|