5种隔离级别
MySQL有4种隔离级别:
读未提交内容(read-uncommitted):
在该隔离级别中,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。
该隔离级别会出现的问题是:脏读(Dirty Read),即读取到了未提交的数据。
读取提交内容(read-committed)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
该隔离级别会出现的问题是:不可重复读(Nonrepeatable Read),即不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
导致这种情况的原因可能有:
1)、有一个交叉的事务有新的commit,导致了数据的改变;
2)、一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
可重复读(repeatable-read)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。
简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。
InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
可串行化(serializable)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
在这个级别,可能导致大量的超时现象和锁竞争。
但由于Spring只提供事务的接口,在具体提交事务的时候必须根据不同的数据库系统进行决定。因此Spring提供了一种默认机制,即根据不同的数据库来选择不同的默认隔离机制。这也是Spring默认的隔离级别。
7种传播机制
所谓的传播机制,就是在面对事务嵌套的时候,Spring采取的处理措施。其有以下7种机制:
REQUIRED 这是默认的。表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)
例如,functionA->functionB,如果functionA是一个事务,functionB也是一个事务并且传播机制为PROPAGATION_REQUIRED,则functionB直接在functionA的事务中运行;那如果functionA不是一个事务,则functionB会新开一个事务。
REQUIRE_NEW 表示当前方法必须运行在它自己的事务中。如果存在当前事务,在该方法执行期间,当前事务会被挂起。
例如,functionA->functionB,如果functionA是一个事务,functionB也是一个事务并且传播机制为REQUIRE_NEW,则functionB会挂起functionA的事务并且开一个新的事物,直到functionB事物执行完毕;如果functionA不是一个事务,则如果functionB开一个新事务执行。
NESTED 如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同
required
的一样。SUPPORTS 表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行。
NOT_SUPPORTED 表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。
MANDATORY 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常。
NEVER 表示当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常。
使用方法
XML方式
xml方式下,需要使用<tx:advice/>
标签进行配置。其具体是在<tx:advice/>
-><tx:attributes/>
-><tx:method/>
中进行属性配置。
其属性有以下:
属性 | 必须 | 默认 | 描述 |
---|---|---|---|
name | Yes | 被该事务关联的方法,可以使用通配符(*),如get* 等。 |
|
propagation | No | REQUIRED | 事务的传播行为(见上列表)。 |
isolation | No | DEFAULT | 事务的隔离级别(见上列表)。 |
timeout | No | -1 | 事务的超时时间(秒),只有传播行为在REQURED和REQUIRES_NEW时有效。 |
read-only | No | falsse | 读写事务与只读事务。仅在REQUIRED或REQUIRES_NEW时有效。 |
rollback-for | No | 触发回滚的错误列表,例如com.foo.MyBusinessException,ServletException 。 |
|
no-rollback-for | No | 不触发回滚的错误列表,例如com.foo.MyBusinessException,ServletException 。 |
最后两个属性值得注意的是:Spring事务默认只会对RuntimeException
或者Error
及其子类进行回滚,其他的异常都不会回滚,只会抛出异常。因此最后两个属性是为了改变这一默认行为的。如遇到IO错误时,Spring事务是不会回滚的,如果需要,则必须配置IOExcetpion
。
注解方式
注解方式下,使用@Transactional
来进行注解的配置。其主要配置都与XML方式属性一致(都含有以上的配置项,不过是以注解属性的形式,横杠写法变为小驼峰写法),主要有以下几个不同的属性,
value
:也就是默认值,这个属性可以用来确定使用不同的事务管理器(transaction manager),即可以在一个应用中使用不同的事物管理器。transactionManager
:与value一致。
值得注意的是:
该注解可以用在类上,则整个类的方法都会使用该事物管理器进行事物操作。
该注解必须是被spring代理的类,否则事务配置无效。
非public方法默认是无法进行事物管理的,加上不会报错,只是会无效。如果想要在非public的方法上使用事务,可以使用在配置类(
@Configuration
)加上@EnableTransactionManagement
注解,并且配置一个transactionAttributeSource
示例。例如:1
2
3
4
5
6
7
8
9
10
11
12/**
* Register a custom AnnotationTransactionAttributeSource with the
* publicMethodsOnly flag set to false to enable support for
* protected and package-private @Transactional methods in
* class-based proxies.
*
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
*/
@Bean
TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(false);
}但是请注意,基于接口的代理中的事务方法必须始终是公共的,并在代理接口中定义。(也就是说,如果spring容器中存储的类型是接口,那么实现类中的事务方法还是必须为公开,并且在接口中进行定义)