自从有了并发,原有的程序哪儿哪儿都很差了——小白语录。
java
并发事务所致使的问题:spring
当同一个应用程序或者不一样应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题sql
并发事务所致使的问题能够分为下面三种类型:数据库
脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但 尚未被提交的字段. 以后, 若 T2 回滚, T1读取的内容就是临时且无效的.并发
不可重复读:对于两个事物 T1, T2, T1 读取了一个字段, 而后 T2 更新了该字段. 以后, T1再次读取同一个字段, 值就不一样了.app
幻读:对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 而后 T2 在该表中插入了一些新的行. 以后, 若是 T1 再次读取同一个表, 就会多出几行.框架
(本文出自:http://my.oschina.net/happyBKs/blog/513441)性能
事务的隔离级别优化
从理论上来讲, 事务应该彼此彻底隔离, 以免并发事务所致使的问题. 然而, 那样会对性能产生极大的影响, 由于事务必须按顺序运行. spa
在实际开发中, 为了提高性能, 事务会以较低的隔离级别运行.
事务的隔离级别能够经过隔离事务属性指定
事务的隔离级别要获得底层数据库引擎的支持, 而不是应用程序或者框架的支持.
Oracle 支持的 2 种事务隔离级别:READ_COMMITED , SERIALIZABLE
Mysql 支持 4 种事务隔离级别.
Read Uncommitted(读取未提交内容)
在该隔离级别,全部事务均可以看到其余未提交事务的执行结果。本隔离级别不多用于实际应用,由于它的性能也不比其余级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它知足了隔离的简单定义:一个事务只能看见已经提交事务所作的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),由于同一事务的其余实例在该实例处理其间可能会有新的commit,因此同一select可能返回不一样结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行。不过理论上,这会致使另外一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另外一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎经过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每一个读的数据行上加上共享锁。在这个级别,可能致使大量的超时现象和锁竞争。
设置隔离事务属性
用 @Transactional 注解声明式地管理事务时能够在 @Transactional 的 isolation 属性中设置隔离级别.
在 Spring 2.x 事务通知中, 能够在 <tx:method> 元素中指定隔离级别
设置回滚事务属性
默认状况下只有未检查异常(RuntimeException和Error类型的异常)会致使事务回滚. 而受检查异常不会.
事务的回滚规则能够经过 @Transactional 注解的 rollbackFor 和 noRollbackFor 属性来定义. 这两个属性被声明为 Class[] 类型的, 所以能够为这两个属性指定多个异常类.
rollbackFor: 遇到时必须进行回滚
noRollbackFor: 一组异常类,遇到时必须不回滚
在 Spring 2.x 事务通知中, 能够在 <tx:method> 元素中指定回滚规则. 若是有不止一种异常, 用逗号分隔.
设置超时和只读事务属性
超时和只读属性能够在 @Transactional 注解中定义.超时属性以秒为单位来计算.
在 Spring 2.x 事务通知中, 超时和只读属性能够在 <tx:method> 元素中进行指定.
咱们在昨天的例子中将买东西事务的方法进行了相关设置,包括事务隔离级别、只读属性、超时timeout
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事务注解 //使用propagation指定事务的传播行为,即当前事务的方法被另外一个事务方法调用时,如何使用是事务。 //使用另一个方法的事务(REQUIRED)?仍是另开一个新事物?(REQUIRES_NEW) //默认REQUIRED //1. REQUIRES_NEW:事务本身的事务,调用该事物方法的事务被挂起 // //2. 用propagation指定事务的隔离级别,最经常使用的是READ_COMMITTED //3. 使用readOnly指定事务是否为只读,表示这个事务只读取数据,不更新数据,这样能够帮助数据库引擎优化事务。 //若是真的是一个只读取数据库值的方法,应该设置readOnly=true //4. 使用timeout设置一个时间限额,单位为秒,若是事务的执行时间超过这个时间阈值,事务将被强制回滚。 @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=false, timeout=5 ) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 获取书的单价 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新书的库存 bookShopDao.updateBookStock(isbn); //3. 更新用户余额 bookShopDao.updateUserAccount(username, price); } }