发布时间:2022年04月15日 04:56:47分享人:蘑菇滴滴来源:互联网21
乐观锁
乐观锁是在同一个数据库事务中我们常采取的策略,因为它能使得我们的系统保持高的性能的情况下,提高很好的并发访问控制。乐观锁,顾名思义就是保持一种乐观的态度,我们认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。它的基本思想就是每次提交一个事务更新时,我们想看看要修改的东西从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败。因为乐观锁其实并不会锁定任何记录,所以数据库的事务隔离级别设置为读取已提交或者更低的隔离界别,那么是不能避免不可重复读问题的(因为此时读事务不会阻塞其它事务),所以采用乐观锁的时候,系统应该要容许不可重复读问题的出现。一般可以采用以下三种方法:版本(Version)字段:在我们的实体中增加一个版本控制字段,每次事务更新后就将版本字段的值加1.时间戳(timestamps):采取这种策略后,当每次要提交更新的时候就会将系统当前时间和实体加载时的时间进行比较,如果不一致,那么就报告乐观锁失败,从而回滚事务或者重新尝试提交。采用时间戳有一些不足,比如在集群环境下,每个节点的时间同步也许会成问题,并且如果并发事务间隔时间小于当前平台最小的时钟单位,那么就会发生覆盖前一个事务结果的问题。因此一般采用版本字段比较好。基于所有属性进行检测:采用这种策略的时候,需要比较每个字段在读取以后有没有被修改过,所以这种策略实现起来比较麻烦,要求对每个属性都进行比较,如果采用hiernate的话,因为Hibernate在一级缓存中可以进行脏检测,那么可以判断哪些字段被修改过,从而动态的生成sql语句进行更新。在JDBC和Hibernate中使用乐观锁:JDBC中使用乐观锁:如果我们采用JDBC来实现持久层的话,那么就可以采用以上将的三种支持乐观锁的策略,在实体中增加一个version字段或者一个Date字段,也可以采用基于所有属性的策略,下面就采用version字段来做一演示:假如系统中有一个Account的实体类,我们在Account中多加一个version字段,那么我们JDBCSql语句将如下写:Select a.version....from Account as a where (wherecondition..)Update Account set version = version+1.....(another field) whereversion =?...(another contidition)可以通过更新结果的行数来进行判断,如果更新结果的行数为0,那么说明实体从加载以来已经被其它事务更改了,所以就抛出自定义的乐观锁定异常(或者也可以采用Spring封装的异常体系)。具体实例如下:123456 | ....... introwsUpdated=statement.executeUpdate(sql); If(rowsUpdated== 0 ){ throwsnewOptimisticLockingFailureException(); } ........ |
在使用JDBCAPI的情况下,需要在每个update语句中,都要进行版本字段的更新以及判断,因此如果稍不小心就会出现版本字段没有更新的问题,相反当前的ORM框架却为我们做好了一切,需要做的就是在每个实体中都增加version或者是Date字段。Hibernate中使用乐观锁:如果采用Hibernate做为持久层的框架,那么实现乐观锁将变得非常容易,因为框架会帮我们生成相应的sql语句,不仅减少了开发人员的负担,而且不容易出错。下面同样采用version字段的方式来总结一下:同样假如系统中有一个Account的实体类,我们在Account中多加一个version字段,1234567 | publicclassAccount{ Longid; ....... @Version //也可以采用XML文件进行配置 Intversion ....... } |
提交事务时,hibernate内部会生成相应的SQL语句将版本字段加1,并且进行相应的版本检测,如果检测到并发乐观锁定异常,那么就抛出StaleObjectStateException.悲观锁
所谓悲观锁,顾名思义就是采用一种悲观的态度来对待事务并发问题,系统中的并发更新会非常频繁,并且事务失败了以后重来的开销很大,这样就需要采用真正意义上的锁来进行实现。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。最后还是需要明确一个问题,假如数据库事务的隔离级别设置为读取已提交或者更低,那么通过悲观锁,控制了不可重复读的问题,但是不能避免幻影读的问题(因为要想避免我们就需要设置数据库隔离级别为Serializable,而一般情况下会采取读取已提交或者更低隔离级别,并配合乐观或者悲观锁来实现并发控制,所以幻影读问题是不能避免的,如果想避免幻影读问题,那么只能依靠数据库的serializable隔离级别(幸运的是幻影读问题一般情况下不严重)。下面就分别以JDBC和Hibernate来总结一下:JDBC中使用悲观锁:在JDBC中使用悲观锁,需要使用selectfor update语句,假如我们系统中有一个Account的类,我们可以采用如下的方式来进行:Select * from Account where ...(where condition).. forupdate.当使用了for update语句后,每次在读取或者加载一条记录的时候,都会锁住被加载的记录,那么当其他事务如果要更新或者是加载此条记录就会因为不能获得锁而阻塞,这样就避免了不可重复读以及脏读的问题,但是其他事务还是可以插入和删除记录,这样也许同一个事务中的两次读取会得到不同的结果集,但是这不是悲观锁所造成的问题,这是数据库隔离级别所造成的问题。最后还需要注意的一点就是每个冲突的事务中,必须使用selectfor update 语句来进行数据库的访问,如果一些事务没有使用selectfor update语句,那么就会很容易造成错误,这也是采用J--DBC进行悲观控制的缺点。Hibernate中使用悲观锁:相比于JDBC使用悲观锁来说,在Hibernate中使用悲观锁将会容易很多,因为Hibernate有API让我们来调用,从而避免直接写SQL语句。下面就Hibernate使用悲观锁做一总结:首先先要明确一下Hibernate中支持悲观锁的两种模式LockMode.UPGRADE以LockMode.UPGRADE_NO_WAIT.(PS:在JPA中,对应的锁模式是LockModeType.Read,这与Hibernate是不一样的呵呵)假如系统中有一个Account的类,那么具体的操作可以像这样:123 | ...... session.lock(account,LockMode.UPGRADE); ...... |
或者也可以采用如下方式来加载对象:1 | session.get(Account. class ,identity,LockMode.UPGRADE). |
这样以来当加载对象时,hibernate内部会生成相应的selectfor update语句来加载对象,从而锁定对应的记录,避免其它事务并发更新。
爱华网本文地址 » http://www.413yy.cn/a/25101015/271195.html
更多阅读
2011/11/25 投资者应该悲观还是乐观?刘军宁心灵可以把地狱变成天堂,也可以把天堂变成地狱。──英国诗人约翰?弥尔顿在圣经旧约中,雅各的儿子约瑟受到多位哥哥的嫉妒,被他们设局推入大坑等死。不久,正好一支商队路过,有人搭救了他,但将他卖
乐观锁乐观锁是在同一个数据库事务中我们常采取的策略,因为它能使得我们的系统保持高的性能的情况下,提高很好的并发访问控制。乐观锁,顾名思义就是保持一种乐观的态度,我们认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重
徐飞又梦见死去的爸爸在喊他。 天刚蒙蒙亮,爸爸把头一天的剩饭热了热,吃完早饭,徐飞就跳上他的自行车后座去学校,爸爸一边骑车一边对他说:“今天好好上学,不要去网吧打游戏了。”还没等徐飞应话,梦就醒了。 外面天刚蒙蒙亮
基于与产业的高相关性,面对不可预知的未来,白酒行业的前景如何,是金融、投资、证券行业等资本市场最为关注的问题。我们认为,总体而言,中国的白酒行业依然拥有美好的前景,这要站到整个产业的高度去看。 平均计算的话,2012年大陆年
心理学家做过这样一个实验:他们让悲观和乐观的人做认知测试。先是让他们做测试的时候眼睛向下看,然后再将眼睛稍向上看。结果发现,当悲观者眼睛向下看时,他们在测试中表现得最好,而乐观者则在向上看时表现最佳。由此看来,悲观者和