数据库锁和业务锁(hibernate等)

共享,更新,乐观,悲观 和 数据库的使用 的两种分类方式

 (1)从数据库系统的角度来看,分为以下三种类型:

 独占(Exclusive Lock)

独占定的资源只允许进行定操作的程序使用,其它任何对它的操作 均不会被接受。执行数据更新命令,即INSERT、UPDATE 或DELETE 命令时,SQL Server 会自动使用独占。但当对象上有其它存在时,无法对其加独占。 独占一直到事务结束才能被释放。

共享(Shared Lock)

共享定的资源可以被其它用户读取,但其它用户不能修改它。在 SELECT 命令执行时,SQL Server 通常会对对象进行共享定。通常加共享 的数据页被读取完毕后,共享就会立即被释放。更新(Update Lock) 更新是为了防止死而设立的。当SQL Server 准备更新数据时,它首先 对数据对象作更新定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新换为独占。但当对象 上有其它存在时,无法对其作更新定。

(2)从程序员的角度看,分为以 下两种类型:

 悲观(Pessimistic Lock) 悲观,正如其名,它指的是对数据被外界(包括本系统当前的其他事务, 以及来自外部系统的事务处理)修改持保守态度,因此在整个数据处理过程中, 将数据处于定状态。悲观的实现,往往依靠数据库提供的机制(也只有数 据库层提供的机制才能真正保证数据访问的排他性,否则,即使在本系统中 实现了加机制,也无法保证外部系统不会修改数据)。

?乐观(Optimistic Lock)

相对悲观而言,乐观机制采取了更加宽松的加机制。悲观大多数 情况下依靠数据库机制实现,以保证操作最大程度的独占性。但随之而来 的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承 受。 而乐观机制在一定程度上解决了这个问题。乐观,大多是基于数据版 本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基 于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段 来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。 此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对, 如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过 期数据。数据库中如何使用首先从悲观开始说。在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=10for 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 种常用的做法来实现: 第一种就是在数据取得的时候把整个数据都copy 到应用中,在进行提交的 时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数 据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业 务逻辑进行解决。 第二种乐观的做法就是采用版本戳,这个在Hibernate 中得到了使用。 采用版本戳的话,首先需要在你有乐观数据库table 上建立一个新的 column,比如为number 型,当你数据每更新一次的时候,版本数就会往上增加 1。比如同样有2 个session 同样对某条数据进行操作。两者都取到当前的数据 的版本号为1,当第一个session 进行数据更新后,在提交的时候查看到当前 数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本 号增加1,这个时候当前数据的版本为2。当第二个session 也更新了数据提交 的时候,发现数据库中版本为 2,和一开始这个 session 取到的版本号不一致, 就知道别人更新过此条数据,这个时候再进行业务处理,比如整个 Transaction 都Rollback 等等操作。在用版本戳的时候,可以在应用程序侧使 用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过 数据库的Trigger 的性能开销还是比较的大,所以能在应用侧进行验证的话还 是推荐不用Trigger。 第三种做法和第二种做法有点类似,就是也新增一个Table 的Column,不 过这次这个column 是采用timestamp 型,存储数据最后更新的时间。在 Oracle9i 以后可以采用新的数据类型,也就是timestamp with time zone 类 型来做时间戳。这种Timestamp 的数据精度在Oracle 的时间类型中是最高的, 精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思 考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没 有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中 数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是 版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有 的程序中,也可以把这个时间戳乐观逻辑写在Trigger 或者存储过程中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值