oracle如何加乐观锁,数据库加乐观锁

Oracle创建悲观锁和乐观锁

为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。

创新互联专注为客户提供全方位的互联网综合服务,包含不限于网站制作、做网站、河曲网络推广、小程序开发、河曲网络营销、河曲企业策划、河曲品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;创新互联为所有大学生创业者提供河曲建站搭建服务,24小时服务热线:18980820575,官方网址:www.cdcxhl.com

数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁。什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。

先从悲观锁开始说。在SqlServer等其余很多数据库中,数据的锁定通常采用页级锁的方式,也就是说对一张表内的数据是一种串行化的更新插入机制,在任何时间同一张表只会插1条数据,别的想插入的数据要等到这一条数据插完以后才能依次插入。带来的后果就是性能的降低,在多用户并发访问的时候,当对一张表进行频繁操作时,会发现响应效率很低,数据库经常处于一种假死状态。而Oracle用的是行级锁,只是对想锁定的数据才进行锁定,其余的数据不相干,所以在对Oracle表中并发插数据的时候,基本上不会有任何影响。

注:对于悲观锁是针对并发的可能性比较大,而一般在我们的应用中用乐观锁足以。

Oracle的悲观锁需要利用一条现有的连接,分成两种方式,从SQL语句的区别来看,就是一种是for update,一种是for update nowait的形式。比如我们看一个例子。首先建立测试用的数据库表。

CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept

这里我们利用了Oracle的Sample的scott用户的表,把数据copy到我们的test表中。首先我们看一下for update锁定方式。首先我们执行如下的select for update语句。

select * from test where id = 10 for update

通过这条检索语句锁定以后,再开另外一个sql*plus窗口进行操作,再把上面这条sql语句执行一便,你会发现sqlplus好像死在那里了,好像检索不到数据的样子,但是也不返回任何结果,就属于卡在那里的感觉。这个时候是什么原因呢,就是一开始的第一个Session中的select for update语句把数据锁定住了。由于这里锁定的机制是wait的状态(只要不表示nowait那就是wait),所以第二个Session(也就是卡住的那个sql*plus)中当前这个检索就处于等待状态。当第一个session最后commit或者rollback之后,第二个session中的检索结果就是自动跳出来,并且也把数据锁定住。不过如果你第二个session中你的检索语句如下所示。

select * from test where id = 10

也就是没有for update这种锁定数据的语句的话,就不会造成阻塞了。另外一种情况,就是当数据库数据被锁定的时候,也就是执行刚才for update那条sql以后,我们在另外一个session中执行for update nowait后又是什么样呢。比如如下的sql语句。 由于这条语句中是制定采用nowait方式来进行检索,所以当发现数据被别的session锁定中的时候,就会迅速返回ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源。所以在程序中我们可以采用nowait方式迅速判断当前数据是否被锁定中,如果锁定中的话,就要采取相应的业务措施进行处理。

select * from test where id = 10 for update nowait

那这里另外一个问题,就是当我们锁定住数据的时候,我们对数据进行更新和删除的话会是什么样呢。比如同样,我们让第一个Session锁定住id=10的那条数据,我们在第二个session中执行如下语句。

update test set value=2 where id = 10

这个时候我们发现update语句就好像select for update语句一样也停住卡在这里,当你第一个session放开锁定以后update才能正常运行。当你update运行后,数据又被你update语句锁定住了,这个时候只要你update后还没有commit,别的session照样不能对数据进行锁定更新等等。

总之,Oracle中的悲观锁就是利用Oracle的Connection对数据进行锁定。在Oracle中,用这种行级锁带来的性能损失是很小的,只是要注意程序逻辑,不要给你一不小心搞成死锁了就好。而且由于数据的及时锁定,在数据提交时候就不呼出现冲突,可以省去很多恼人的数据冲突处理。缺点就是你必须要始终有一条数据库连接,就是说在整个锁定到最后放开锁的过程中,你的数据库联接要始终保持住。与悲观锁相对的,我们有了乐观锁。乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。在乐观锁中,我们有3种

常用的做法来实现。

[1]第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。

[2]第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个session取到的版本号不一致,就知道别人更新过此条数据,这个

时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用Trigger。

[3]第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中。

不知道这个是不是你需要的...

Oracle数据库锁的常用类型有哪些

Oracle数据库的锁类型

根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护数据库的内部结构。

DML锁的目的在于保证并发情况下的数据完整性,本文主要讨论DML锁。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。

当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。如表1所示。

在数据行上只有X锁(排他锁)。在 Oracle数据库中,当一个事务首次发起一个DML语句时就获得一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行DML语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX锁被释放,其他会话才可以加锁。

当Oracle数据库发生TX锁等待时,如果不及时处理常常会引起Oracle数据库挂起,或导致死锁的发生,产生ORA-60的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。

TX锁等待的分析

在介绍了有关地Oracle数据库锁的种类后,下面讨论如何有效地监控和解决锁等待现象,及在产生死锁时如何定位死锁的原因。

监控锁的相关视图 数据字典是Oracle数据库的重要组成部分,用户可以通过查询数据字典视图来获得数据库的信息。和锁相关的数据字典视图如表2所示。

TX锁等待的监控和解决在日常工作中,如果发现在执行某条SQL时数据库长时间没有响应,很可能是产生了TX锁等待的现象。为解决这个问题,首先应该找出持锁的事务,然后再进行相关的处理,如提交事务或强行中断事务。

死锁的监控和解决在数据库中,当两个或多个会话请求同一个资源时会产生死锁的现象。死锁的常见类型是行级锁死锁和页级锁死锁,Oracle数据库中一般使用行级锁。下面主要讨论行级锁的死锁现象。

当Oracle检测到死锁产生时,中断并回滚死锁相关语句的执行,报ORA-00060的错误并记录在数据库的日志文件alertSID.log中。同时在user_dump_dest下产生了一个跟踪文件,详细描述死锁的相关信息。

在日常工作中,如果发现在日志文件中记录了ora-00060的错误信息,则表明产生了死锁。这时需要找到对应的跟踪文件,根据跟踪文件的信息定位产生的原因。

如果查询结果表明,死锁是由于bitmap索引引起的,将IND_T_PRODUCT_HIS_STATE索引改为normal索引后,即可解决死锁的问题。

表1 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

表2 数据字典视图说明

视图名 描述 主要字段说明

v$session 查询会话的信息和锁的信息。 sid,serial#:表示会话信息。

program:表示会话的应用程序信息。

row_wait_obj#:表示等待的对象。

和dba_objects中的object_id相对应。

v$session_wait 查询等待的会话信息。 sid:表示持有锁的会话信息。

Seconds_in_wait:表示等待持续的时间信息

Event:表示会话等待的事件。

v$lock 列出系统中的所有的锁。 Sid:表示持有锁的会话信息。

Type:表示锁的类型。值包括TM和TX等。

ID1:表示锁的对象标识。

lmode,request:表示会话等待的锁模式的信

息。用数字0-6表示,和表1相对应。

dba_locks 对v$lock的格式化视图。 Session_id:和v$lock中的Sid对应。

Lock_type:和v$lock中的type对应。

Lock_ID1: 和v$lock中的ID1对应。

Mode_held,mode_requested:和v$lock中

的lmode,request相对应。

v$locked_object 只包含DML的锁信息,包括回滚段和会话信息。 Xidusn,xidslot,xidsqn:表示回滚段信息。和

v$transaction相关联。

Object_id:表示被锁对象标识。

Session_id:表示持有锁的会话信息。

Locked_mode:表示会话等待的锁模式的信

息,和v$lock中的lmode一致。

col owner for a12

col object_name for a16

select b.owner,b.object_name,l.session_id,l.locked_mode

from v$locked_object l, dba_objects b

where b.object_id=l.object_id;

select t2.username,t2.sid,t2.serial#,t2.logon_time

from v$locked_object t1,v$session t2

where t1.session_id=t2.sid order by t2.logon_time;

如果有长期出现的一列,可能是没有释放的锁。我们可以用下面SQL语句杀掉长期没有释放非正常的锁:

alter system kill session 'sid,serial# ';

如果出现了锁的问题, 某个DML操作可能等待很久没有反应。

当你采用的是直接连接数据库的方式,也不要用OS系统命令 $kill process_num 或者 $kill -9 process_num来终止用户连接,因为一个用户进程可能产生一个以上的锁, 杀OS进程并不能彻底清除锁的问题

Oracle锁表(死锁) 2011-05-03 17:46:41| 分类: Java技术 | 标签: |字号大中小 订阅 .

数据库与操作系统一样,是一个多用户使用的共享资源。 当多个用户并发地存取数据时,在数据库中就会发生多个事务同时存取同一数据地情况。 若对并发操作不加控制就可能会读取和存储不正确地数据,破坏数据库地一致性。 加锁时实现数据库并发控制地一个非常重要地技术。 在实际应用中经常会遇到地与锁相关地异常情况,当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就会出现死锁,严重影响应用的正常执行。

在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(即S锁)。当数据对象被加上排它锁时,其他的事务不能不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

死锁的第一种情况:

一个用户A访问表A(锁住了表A),然后又访问表B; 另一个用户B访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁产生了。

解决方法:

这种死锁比较常见,是由于程序的BUG产生的,除了调整程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照同样的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理,必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

死锁的第二种情况

用户A查询一条记录,然后修改该条记录;这时用户B修改该条记录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在必须等A释放掉共享锁,而A由于B的独占锁而无法上升到独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项目种经常发生,如在某项目中,页面上的按钮点击后,没有使按钮立刻失效,使得用户会多次快速点击同一按钮,这样同一段代码对数据库同一条记录进行多次操作,很容易就出现这种死锁的情况。

解决方法:

1、对于按钮等控件,点击后使其立刻失效,不让用户重复点击,避免对同时对同一条记录操作。

2、使用乐观锁进行控制。乐观锁大多是基于数据版本(version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库增加一个“version”字段来实现。读取处数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交的数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据库加锁开销(用户A和用户B操作过程中,都没有对数据库加锁),大大提升了大并发量下的系统整体性表现。 Hibernate在其数据访问引擎中内置了乐观锁实现。需要注意的是,由于乐观锁机制是我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。

3、使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的select.......for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这样的情况将导致灾难性的结果。所以,采用悲观锁进行控制时一定要考虑清楚。

死锁的第三种情况

如果在事务种执行了一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行之后,就很容易发生死锁和阻塞。类似的情况还有当表种的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。

解决方法:

SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行 分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

***查询死锁表以及解锁表***

通过select * from v$locked_object

可以获得被锁的对象的object_id及产生锁的会话sid,通过查询结果中的object_id,可以查询到具体被锁的对象。

锁有以下几种模式:

0:none

1:null 空

2:Row-S 行共享(RS / S锁):共享表锁

3:Row-X 行专用(RX / X锁):用于行的修改

4:Share 共享锁(S):阻止其他DML操作

5:S/Row-X 共享行专用(SRX):阻止其他事务操作

6:exclusive 专用(X):独立访问使用

数字越大锁级别越高, 影响的操作越多。

一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。

select ... from ... for update; 是2的锁。

当对话使用for update子串打开一个游标时,

所有返回集中的数据行都将处于行级(Row-X)独占式锁定,

其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。

insert / update / delete ... ; 是3的锁。

没有commit之前插入同样的一条记录会没有反应,

因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。

创建索引的时候也会产生3,4级别的锁。

locked_mode为2,3,4不影响DML(insert,delete,update,select)操作,

但DDL(alter,drop等)操作会提示ora-00054错误。

有主外键约束时 update / delete ... ; 可能会产生4,5的锁。

DDL语句时是6的锁。

以DBA角色, 查看当前数据库里锁的情况可以用如下SQL语句:

select object_id,session_id,locked_mode from v$locked_object;

select t2.username,t2.sid,t2.serial#,t2.logon_time

from v$locked_object t1,v$session t2

where t1.session_id=t2.sid order by t2.logon_time;

如果有长期出现的一列,可能是没有释放的锁。

我们可以用下面SQL语句杀掉长期没有释放非正常的锁:

.NET多个用户对oracle数据库的同一张表进行插入,发现有的用户出现插入不进的情况,怎么解决?加锁怎么加

可以用乐观锁,在表上面加个字段rowversion,假如初值是1

当一个用户去更新表时,

1)先获取这个字段的值1

2)然后更新数据,rowversion+1,

3)提交时,先判断rowversion是否等于1,

如果不等于,说明有别的用户已经更改过该行数据了。就从1)开始在做一遍。

如果等于,就提交(此时rowversion=2)

你可以查查乐观锁的相关知识。

ORACLE里几种锁模式

ORACLE锁具体分为以下几类:

1.按用户与系统划分,可以分为自动锁与显示锁

自动锁:当进行一项数据库操作时,缺省情况下,系统自动为此数据库操作获得所有有必要的

显示锁:某些情况下,需要用户显示的锁定数据库操作要用到的数据,才能使数据库操作执行得更好,显示锁是用户为数据库对象设定的。

2.按锁级别划分,可分为共享锁与排它锁

共享锁:共享锁使一个事务对特定数据库资源进行共享访问——另一事务也可对此资源进行访问或获得相同共享锁。共享锁为事务提供高并发性,但如拙劣的事务设计+共享锁容易造成死锁或数据更新丢失。

排它锁:事务设置排它锁后,该事务单独获得此资源,另一事务不能在此事务提交之前获得相同对象的共享锁或排它锁。

3.按操作划分,可分为DML锁、DDL锁

+DML锁又可以分为,行锁、表锁、死锁

-行锁:当事务执行数据库插入、更新、删除操作时,该事务自动获得操作 表中操作行的排它锁。

-表级锁:当事务获得行锁后,此事务也将自动获得该行的表锁(共享锁),以防止其它事务进行DDL语句影响记录行的更新。事务也可以在进行 过程中获得共享锁或排它锁,只有当事务显示使用LOCK TABLE语 句显示的定义一个排它锁时,事务才会获得表上的排它锁,也可使用

LOCK TABLE显示的定义一个表级的共享锁(LOCK TABLE具体用法请参 考相关文档)。

-死锁:当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就 出现死锁。

如事务1在表A行记录#3中有一排它锁,并等待事务2在表A中记录#4 中排它锁的释放,而事务2在表A记录行#4中有一排它锁,并等待事务 1在表A中记录#3中排它锁的释放,事务1与事务2彼此等待,因此就造 成了死锁。死锁一般是因拙劣的事务设计而产生。

死锁只能使用SQL下:alter system kill session 'sid,serial#';

或者使用相关操作系统kill进程的命令,如UNIX下kill -9 sid,或者 使用其它工具杀掉死锁进程。

+DDL锁又可以分为:排它DDL锁、共享DDL锁、分析锁

-排它DDL锁:创建、修改、删除一个数据库对象的DDL语句获得操作对象的 排它锁。

如使用alter table语句时,为了维护数据的完成性、一致性、

合法性,该事务获得一排它DDL锁。

-共享DDL锁:需在数据库对象之间建立相互依赖关系的DDL语句通常需共享

获得DDL锁。

如创建一个包,该包中的过程与函数引用了不同的数据库表,

当编译此包时,该事务就获得了引用表的共享DDL锁。

-分析锁:ORACLE使用共享池存储分析与优化过的SQL语句及PL/SQL程序,使

运行相同语句的应用速度更快。一个在共享池中缓存的对象获得

它所引用数据库对象的分析锁。分析锁是一种独特的DDL锁类型,

ORACLE使用它追踪共享池对象及它所引用数据库对象之间的依赖 关系。当一个事务修改或删除了共享池持有分析锁的数据库对象

时,ORACLE使共享池中的对象作废,下次在引用这条SQL/PLSQL语 句时,ORACLE重新分析编译此语句。

4.内部闩锁

内部闩锁:这是ORACLE中的一种特殊锁,用于顺序访问内部系统结构。

当事务需向缓冲区写入信息时,为了使用此块内存区域, ORACLE首先必须取得这块内存区域的闩锁,才能向此块内存写入

信息。


网站标题:oracle如何加乐观锁,数据库加乐观锁
网站地址:http://hbruida.cn/article/dsiidpe.html