数据库事务处理


1.事务(transaction)

事务是这样一种机制,它确保多个Sql语句被当作单个工作单元来处理。事务具有以下的作用:一致性:同时进行的查询和更新彼此不会发生冲突,其他用户不会看到发生了变化但尚未提交的数据。
可恢复性:一旦发生系统故障,数据库会自动地完全恢复未完成的事务。
事务被认为是一系列操作的集合,是一个工作单元(unit of work),事务的处理的结果有两个一个是commit,另一个是roll back,事务有ACID特性(atomicity,consistency,isolation, durability)。atomicity是说明事务处理过程中不能够被打断;consistency是指事务所操作的数据是一个一致性的集合,也就是这些数据对于其他并发进行的事务是隐藏的;isolation是指两个并发进行的事务相互之间是不可见的,也就是互不影响;durability是指事务处理的结果将被持久化。
在分析事务处理时,要将原来的单个sql的commit,rollback和对事务的commit,rollback区分开来,一个事务可能是由多个sql操作所组成的。

2.并发事务执行时引起的问题
(1)lost update(丢失修改): A和B事务并发执行,A事务执行update后,提交;B事务在A事务update后,B事务结束前也做了对同样数据的update操作,然后rollback,则两次提交操作都丢失了。
(2)dirty read(脏读):A和B事务并发执行,B事务执行update后,A事务select B事务没有提交的数据,B事务roolback,则A事务得到的数据不是数据库中的真实数据。也就是dirty data(和数据库中不一致的数据)
(3)unrepeatable read(读的不可重复性):A和B事务并发执行,A事务select数据,然后B事务update该数据,A再次select该数据时,发现该数据变化了。(second lost updates:是unrepeatable read的一种特殊情况,即A事务update数据,然后B事务update数据,A事务select发现自己update的数据变了,该问题为什么不划分到第一类问题,是和事务的隔离级别相关的,也就是A的select执行时允许了并发的B的update的执行,隔离级别3能够解决该问题,而隔离级别1不能解决该问题)
(4)phantom read:A和B事务并发执行,A事务select数据,B事务insert或者update数据,A事务再次select发现结果集中有以前没有的数据或者以前有的数据消失了。
提示:下方中的数字指代上文中的(2.并发事务执行时引起的问题)
3.事务的隔离级别
在企业应用中,事务往往存在于并发执行的环境当中,那么如果做到并发事务之间完全的isolation,做到对于以上四大问题完全解决,那么势必会影响程序的performance和scalability,因此减弱这种完全的isolation,而带来的就是performance和scalability的提高。因此事务的隔离级别根据以前的四大问题有四种。
(1)read uncommitted isolation:不允许(1)问题存在,允许(2)问题存在。写事务执行时不允许同时有其他的写事务并发执行。
(2)read committed isolation:不允许(2)问题存在,允许(3)问题存在。读事务允许其他事务并发执行,没有提交的写事务执行时不允许同时有其他并发事务执行。
(3)repeatable read isolation:不允许(3)问题存在,允许(4)问题存在。读事务不允许写事务并发执行,没有提交的写事务执行时不允许同时有其他并发事务执行。
(4)serializable:完全解决问题。
提示:下方中的数字指代上文中的(2.并发事务执行时引起的问题)
4.Hibernate的折衷解决方案
一般的应用作隔离级别时,往往采用(2),(3)两种,(1),(4)两种前者轻后者重,但是Hibernate为了提高performance和scalability,在数据库一层中采用的是(2)的隔离级别,然后在程序中进行控制,从而实现了(3)的隔离级别,因此提高了企业应用的事务处理效率。当然Hibernate对于数据库一层的隔离级别也可以显示指定。
Hibernate在程序中的控制方法有:version number, timestamp,对于遗留database也有optimistic-lock,而version number有时不能解决特殊的事务并发引起来的(3)问题,那么就需要针对特殊情况进行细粒度的事务控制,可以看一下LOCKMODE。


5.光标带来的当前值的混乱
  事务在执行过程中它在某个表上的当前查找位置是由光标表示的。光标指向当前正处理的记录。当处理完该条记录后,则指 向下一条记录。在多个事务并发执行时,某一事务的修改可能产生负作用,使与这些光标有关的事务出错。

6. 未释放修改造成连锁退出

  一个事务在进行修改操作的过程中可能会发生故障,这时需要将已做的修改回退(Rollback)。如果在已进行过或已发现错误尚未复原之前允许其它事务读已做过修改(脏读),则会导致连锁退出。

7. 一事务在对一表更新时,另外的事务却修改或删除此表的 定义。

  数据库会为每个事务自动地设置适当级别的锁定。对于前面讲述的问题:脏读、未释放修改造成的连锁退出、一事务在对一表更新时另外的事务却修改或删除此表的定义,数据库都会自动解决。而另外的三个问题则需要在编程过程中人为地定义事务或加锁来解决。


先写这些,如果大家感兴趣,我可以再把Hibernate具体的解决方案总结归纳。

以上介绍了事务以及并发事务处理时的问题,和针对于这些问题在数据库层所能做的隔离级别,但是在选择read commit和repeatable read两个隔离级别时,如果考虑到performance和scalability,可以选择一个折衷的方案,也就是在数据库中的隔离级别选择read commit,而通过对应用程序的控制,可以达到repeatable read的效果。Hibernate在程序中控制并发的事务处理上,也有自己的方法,本文在Hibernate与事务处理基础上,对Hibernate的这些并发事务处理方法进行介绍。
数据库隔离级别可以通过设置hibernate的属性文件,来改变数据库默认的隔离级别:

xml 代码
  1. 1:Read umcommited  
  2. 2:Read commited  
  3. 3:Repeatable read  
  4. 4:Serializable  


在这里设置hibernate.connection.isolation=2;但需要注意的是,如果hibernate用在了应用服务器上,该应用服务 器对数据库连接提供了受管理的控制,则这里设置的isolation级别失效,采用的是app server定义的级别,可以通过改变app server的配置来改变该隔离级别。
因为这里设置的隔离级别是read commit,那如果想达到repeatable read,则需要设置version number或者timestamp。

如果某个项目从头到尾开始设计和开发,考虑到事务处理,则在需要并发控制的数据库表字段上,添加和hibernate的version number相对应的字段。例如对Student表的并发控制,则需要在TSTUDENT表上添加STU_VERSION字段,在ORM的映射文件中需要在标识符属性下添加,例如:

java 代码
  1. <class name="Student" table="TSTUDENT">  


  2. ...  
  3. class>  


在Student.java的pojo文件中添加:

java 代码
  1. publicclass Student {  
  2. ....  
  3. privateint version;  
  4. ...  
  5. }  


version属性是不能被应用所改变的,所以仅仅提供get方法即可。但是version number是怎样在应用层提供对事务的并发处理机制呢?原理是这样的:在hibernate的事务A(在这里假设事务的范围和Session范围一致,每个Session对应一个persistent context)中,执行每一个DML操作,都会先检查一下对象的version属性,假如从数据库中得到的student实例的version属性是1,此时student已经加载到Session对应的persistent context中,如果对student对象进行改变,在Session.flush()的时候,则将persistent context中的student对象的version属性设置为2,然后在保存student对象时,将对象对应的记录version字段更新为2。
注意的是,如果并发的另一个事务B在事务A进行保存操作之前,已经将version更新为2或者更高值的student存入数据库,那A在保存student之前,需要检查该persistent object对应的记录的version为1(因为在从数据库取得persistent object的时候,version为1)的行的个数,如果行数为0,则抛出 StaleObjectStateException,表明存在其他的事务更新了数据库,那这样的话,A事务的更新操作就不会覆盖B事务的更新操作,达到了避免(3)问题中的second lost update(见Hibernate与事务处理)。
当然避免了second lost update是不够的,因为是不是这样就能够具有repeatable read的隔离级别呢?比较一下read commit和repeatable read的隔离级别的差别,read commit是读事务允许其他事务并发执行,而repeatable read是读事务不允许写事务并发执行,在hibernate中,通过Session对应的persistent context来实现后者,具体是在一个事务A中(假设事务的范围和Session范围一致),读取student对象到persistent context,此时事务B更改了student对象对应的记录并进行持久化,根据数据库的默认隔离级别read commit,那么A事务如果再读取student对象,那就是产生unrepeatable read,其实不然,正是因为hibernate的persistent context,使得A事务再次读取student不是从数据库中读取,而是从当前的Session读取,因此避免了unrepeatable read,同时使得具有类似于数据库隔离级别repeatable read的隔离效果。

当然我们这里假设的是事务的范围和Session范围一致,但是当用户操作中途停留时间过长,则需要将不同的事务在同一conversation下实现,要保证不同事务共用Session可以通过Hibernate的FlushMode来实现。当然也可以保证Session和事务的对应,通过在不同Session中传递detached object来实现。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值