在开发多用户、数据库驱动应用的时候,最大的难点之一应该时间争取完成最大限度的并发访问,与此同时还要确保每一个用户能以一致的方式读取和修改数据,为此就有了锁定机制。
锁机制用于管理对共享资源的并发访问,Oracle会在行级对表数据锁定,也会在其他多个级别上使用锁,从而对多种不同资源提供并发访问。
在讨论Oracle的各类型锁之前,我们先来讨论一下,如果没有正确使用锁定机制可能会产生什么问题。
简单来说就是出现一次啊情况时,就会发生丢失更新:
悲观锁定即悲观的认为自己在获取数据库某一行后,一定会有人对该行进行修改,所以在查询完或会立刻加上行锁,例如:
用户一但点击更新某一行,那么在他选择好要更新的行后,就会给该行添加上一个行锁,这个行锁会持续到他修改完数据并且提交后才会释放。
在加上锁定后就可以非常安全的修改这一行,就不会覆盖其他人所做的修改。
第二种方法称为乐观锁定,就是在获取数据的时候很乐观的认为数据不会被其他用户修改,会在执行更新的时候才去判断数据是否被修改过。
这种锁定方法,用户执行更新失败的可能性就很大,在执行更新的时候发现数据被修改过了就需要重头再来。
实现乐观锁的方式有很多种,即应用会存储行所有的前映像,比如:
使用一个特殊的列,列由一个数据库触发器或者应用程序代码维护,可以告诉我们该行的版本从而确定是否被修改过。
或者使用一个校验或者散列值,使用原来的数据计算得出的。
这两种方法选择哪种好呢。悲观锁在Oracle中工作的很好,但是需要与数据库有一条状态的连接,不能跨连接,所有如果有大量用户的情况下,悲观锁不太现实。而乐观锁就不存在这个问题。在使用乐观锁就可能会出现用户修改完想执行更新的时候发现数据被其他用户修改了,这时候用户就需要重新获取数据进行修改。对用户体验不好。所有在用户小的时候可以使用悲观锁,用户多的时候还是使用乐观锁。
如果一个用户会话持有某个资源的锁,那么另一个会话请求的时候就会出现阻塞,直到锁被释放。
数据库中有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,所有可以同时使用这两种技术。
如果两个会话,每个会话都持有另一会话想要的资源,那么此时就会出现死锁。例如会话A更新表A,会话B更新表B,这时候如何会话A也更新表B,就会阻塞应用会话B已经锁定了表B,这不是死锁只是阻塞而已,而如果会话B也去更新表A这时候就导致了死锁。
Oracle中主要有3类锁
DML锁确保一次只有一个人能修改某一行。而且你正在处理一个表时,其他人不能删除这个表。
1、TX锁
事物发起第一个修改的时候就会获取一个TX锁(事物锁),直到事物提交或者回滚。TX锁用作一种排队机制,是的其他事物会等待这个事物执行,在事物中修改或者通过select for update都会指向该事物的一个相关的TX锁,并且ORACLE使用闩这个数据的一个属性来进行加锁而不是使用传统的锁管理器。在Oracle加锁的时候只需要找到想锁定的那一行,然后锁定这一行。不需要去专门的列表中去查询这一行是否被锁定。
有意思的是,当你找到数据的是他可能看上去石碑锁定的但是实际上并没有被锁定。Oracle对事物进行锁定的时候,行会指向事物的ID,当另一个会话到来的时候,他就会看多锁住该行的ID,并判断该事物是否是活动的,如果锁不活的则会允许访问这个事物,如果锁是活动的就会要求一但释放就得到通知。
2、TM(DML Enqueue)锁
TM锁用于确保在修改表的内容时,表的结构不会改变,如果已经更新了一个表,就会得到这个表的一个TM锁,会防止另一个用户在该表上执行DROP或者ALTER命令。
在DDL操作中会自动为对象加上DDL锁,一但操作执行就会立刻是否DDL锁。
有以下三种类型的DDL锁:
大多数DDL都带有一个排他的DDL锁。
另一类DDL会获得共享DDL锁,在创建存储的编译对象(如过程或者视图)时,会对依赖的对象家这种共享的DDL锁。例如:
Create view MyView
as
pno,emp,ename,dept.deptno,dept.dname
form emp,dpt
where emp.deptno=dept.deptno
在create view执行的时候,表emp和dep都会加上共享DDL锁。
最后一类可中断解析锁,当你的会话解析一条语句的时候,对于该语句引用的每一个对象都会加上解析锁,目的是为了,如果以某种方式删除或者修改一个被引用的对象,可以将共享池中已解析的缓存语句置为无效。
本文发布于:2024-02-03 05:10:29,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170690822948865.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |