`
dannyfk221
  • 浏览: 5095 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
最近访客 更多访客>>
社区版块
存档分类
最新评论

hibernate乐观锁

阅读更多

 

乐观锁( Optimistic Locking

相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过 程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作 员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。

乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本 Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 version 字段来实现。

读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100

1 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50 $100-$50 )。

2 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 $100-$20 )。

3 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2

4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 提交版本必须大于记

录当前版本才能执行更新 的乐观锁策略,因此,操作员 B 的提交被驳回。

这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。

从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A 和操作员 B 操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。

需要注意的是,乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户余额更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。在系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)。

Hibernate 在其数据访问引擎中内置了乐观锁实现。如果不用考虑外部系统对数据库的更新操作,利用 Hibernate 提供的透明化乐观锁实现,将大大提升我们的生产力。

 

 

好,废话不多说了,现在我要为 TMoney 表配置乐观锁

 

首先,在数据库中建立一个 version 字段,设置为 int 类型(注意: version 字段不能为空)

 

打开 TMoney.hbm.xml ,如下图设置

 

dynamic-update="true" dynamic-insert="true" optimistic-lock="version"

 

<version column="version" name="version" type="java.lang.Integer" />
 

 

 


接着打开 TMoney.java

 

定义一个 version 变量,类型为 int ,并生成 get set 方法

 

到此,配置完成。

 

好,现在我们来测试一下成果。

 

 

public void test() throws Exception {
	TMoney money = new TMoney(); //定义,不解释
	TMoney money2 = new TMoney(); //定义,不解释

	money = this.dao.doFinByid(1); //查询某一条记录
	money2 = this.dao.doFinByid(1); //查询某一条记录

	//第一个人汇款100
	int sum = 100 + money.getSum();
	money.setSum(sum);

	//第二个人汇款200
	int sum2 = 200 + money2.getSum();
	money2.setSum(sum2);

	//修改,不解释
	this.dao.doUpdate(money);
	this.dao.doUpdate(money2);
}
 

 

 

 

 

 

 

  • 大小: 16.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics