spring 使用的是数据库默认的spring 事务隔离级别别吗

数据库事务的隔离级别简单总结
中事务的隔离级别有四种,级别由低到高分别为:Read uncommitted、Read committed、 Repeatable read和Serializable 四种。这四个级别可以逐个解决脏读、不可重复读、幻读这几个问题。
如果当前隔离级别为Read uncommitted时,可能出现脏读,即可能读到还未提交的临时写数据。
如果当前隔离级别为Read committed时,能够避免脏读,但可能造成不可重复读,即两个事务同时对同一个数据进行修改时,发生的数据一致性和完整性的破坏。大部分的数据库的默认级别都是Read committed,如SQL Server和。
如果当前隔离级别为Repeatable read时,可以避免不可重复读,即当前有两个及以上数量的事务读取同一数据时,数据库选择对第一个访问该数据的事务可读可写,对第二条及其后的事务为只读。尽管这样可以避免不可重复读,但是当事务A两次读取同一个数据时,如果期间有其他对数据库可读可写的事务对数据进行修改时,那么事务A两次读取数据得到的结果极有可能是不一致的,即造成了幻读。
Serializable序列化时数据库中最高级别的事务隔离级别,在该级别下,事务按照顺序执行,这样能够避免脏读、不可重复读和幻读,但所造成的花销大、性能低下。博客分类:
今天看了在论坛上看了一篇文章,说再也不招精通SSH的人了,上面写面试了一个好称精通人spring的人却不知道spring里面事务的传播属性和事务隔离级别,本来自以为spring还是挺熟悉的,不过现在发现做了快2年的开发了,还没有一件称得上精通的技术。 网上查了一下关于spring的事务文章: 一、Propagation (事务的传播属性)
Propagation :  key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。 PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。 1: PROPAGATION_REQUIRED 加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候, ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA 的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。 这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被 提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚 2: PROPAGATION_SUPPORTS 如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行 3: PROPAGATION_MANDATORY 必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常 4: PROPAGATION_REQUIRES_NEW 这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW, 那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后, 他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在 两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚, 如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。 5: PROPAGATION_NOT_SUPPORTED 当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED , 那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。 6: PROPAGATION_NEVER 不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER , 那么ServiceB.methodB就要抛出异常了。 7: PROPAGATION_NESTED 理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立, 而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。 而Nested事务的好处是他有一个savepoint。 ***************************************** ServiceA { /** * 事务属性配置为 PROPAGATION_REQUIRED */ void methodA() { try { //savepoint ServiceB.methodB(); //PROPAGATION_NESTED 级别 } catch (SomeException) { // 执行其他业务, 如 ServiceC.methodC(); } } } ******************************************** 也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如 ServiceC.methodC,继续执行,来尝试完成自己的事务。 但是这个事务并没有在EJB标准中定义。 二、Isolation Level(事务隔离等级): 1、Serializable:最严格的级别,事务串行执行,资源消耗最大; 2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。 3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。 4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。 我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。 这里就不阐述。 我们首先说并发中可能发生的3中不讨人喜欢的事情 1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。 2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。 3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。
Dirty reads
non-repeatable reads
Serializable
REPEATABLE READ
READ COMMITTED
Read Uncommitted
三、readOnly 事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。
这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
四、Timeout
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
浏览: 160667 次
来自: 上海
finallygo 写道这个必须支持啊,希望作者不断完善谢谢支 ...
这个必须支持啊,希望作者不断完善
refusedlazy 写道refusedlazy 写道ref ...
refusedlazy 写道refusedlazy 写道ref ...
refusedlazy 写道refusedlazy 写道ref ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'mysql+Spring数据库隔离级别与性能分析
字体:[ ] 类型:转载 时间:
数据库隔离级别与Spring配置事务的联系及性能影响,以下是个人理解,如果有瑕疵请及时指正
这里以mysql为例,先明确以下几个问题:
一.一般项目如果不自己配置事务的话,一般默认的是autocommit,即执行完一个操作后自动commit,提交事务。
(注:事务是绑定在数据库操作上的,也就是当程序执行(statement.excute等操作)转而到数据库层面上的时候,事务才开始发生)当然spring可以将几个数据库操作动作绑在一个事务中,这样就需要介绍下spring事务配置方法,下面介绍的是常用方法,其他方法网上有很多。spring提供了很多事务配置的策略,很方便,简要介绍一下:
代码如下:&&property name="transactionAttributes"& &props& &prop key="save*"&PROPAGATION_REQUIRED&/prop& &prop key="update*"&PROPAGATION_REQUIRED&/prop& &prop key="delete*"&PROPAGATION_REQUIRED&/prop& &prop key="get*"&PROPAGATION_REQUIRED,readOnly&/prop& &prop key="find*"&PROPAGATION_REQUIRED,readOnly&/prop& &/props&
一般spring配置事务都是以上的配法,具体参数的意思有不懂的上网自己查吧,那么需要注意以下几点:(题外话)1.我习惯将事务配置在service上,这时需要注意,只有service中以save、update等开头的方法,配置的事务才有效果。如果service中的方法名不是以save等开头的,比如taskSave()方法,即使在实现类中调用了service中的update方法,配置事务也失效,我试过。2.readOnly这个属性很有意思,因为用了它后,会自动将数据库的隔离级别提高了一级,由提交读变为重复读,这块我后面说明。
二.数据库隔离级别
数据库隔离级别主要有以下四个:不可提交读,提交读,重复读和序列化读(以下理解可以不看)。1. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。 &&&& 这种隔离级别会产生脏读,不可重复读和幻像读。 2. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据 3. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 &&&& 它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 4. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 &&&& 除了防止脏读,不可重复读外,还避免了幻像读。mysql默认的隔离级别是重复读,即 ISOLATION_REPEATABLE_READ。
注意:其中未提交读与序列化读不常用,未提交读危险性太高,会读到很多脏数据。而可串行化读是通过将读取的每一行数据加锁,以耗费性能为代价换取的,所以使用也很少,大部分数据库的隔离级别是提交读,比如oracle、sqlserver。而mysql默认的数据隔离级别是可重复读。
下面我来结合项目分析以下调整数据库隔离级别对性能的影响:本地mysql数据库由ISOLATION_REPEATABLE_READ级别降低到ISOLATION_READ_COMMITTED级别:
场景:未用Spring,用户A在一个事务中对数据库发出两次查询请求,在两次查询之间,用户B对数据库的记录进行修改。
结果:ISOLATION_REPEATABLE_READ级别:用户A两次查询结果不一样。
&&&&&&&&& ISOLATION_READ_COMMITTED级别:用户A两次查询结果一样,因为对记录进行了加锁操作。
以task模块为例,在本地运行任务首页,通过对比分析两种事务处理方式得到如下结果(每次统计数据前均清理浏览器缓存,统计3次取平均值):发现降低数据库事务的隔离级别,对于一些特殊逻辑的操作上,性能有所提升。 但是如果查询过程中,不涉及同一事务中多次对数据库操作的复杂逻辑及同一事务中多次查询同一结果集的逻辑,则对速度的提升效果并不明显,即事务进行时对数据集加锁的时间是可以忽略的,下面再来理解一下事务隔离级别与锁的关系。 谈到数据库隔离级别,就要说一下锁的概念: 主要分为共享锁和排他锁。 共享锁:由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写 排它锁:由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁,典型是mysql事务中。 个人理解:共享锁和排他锁没有严格的界限,我认为应该通过结果确定加的是共享锁还是排他锁。 例如:用户A修改一条数据,用户B也修改这条数据,挂起。 但是B查看这个数据可以,证明A用户添加了行级共享锁。 再例如:用户A修改一条数据,用户B查询这条数据失败,查询其他数据也失败。那么肯定A加了表级排他锁。 再例如:用户A修改一条数据,用户B查询记录可以,但是修改这条记录不行,修改其他记录也不行,那么A加了表级共享锁。 不同的数据隔离级别,加的锁是不一样的。 回到前面的问题,readonly属性一旦被设置后,数据库级别如果为提交读,那么同一个事务中,如果对两次结果集进行查询,中间间隔修改数据库,那么应该会是同一个结果集,相当于查询的时候采用的是重复读的隔离级别。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具博客分类:
先看下Spring的事务传播行为类型
事务传播行为类型
PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制。
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
在xml中的设置应该是 timeout_11 表示超时为11秒。。
为什么呢。。看下面的源码可知。。
* PropertyEditor for TransactionAttribute objects. Takes Strings of form
* &p&&code&PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2&/code&
* &p&where only propagation code is required. For example:
* &p&&code&PROPAGATION_MANDATORY,ISOLATION_DEFAULT&/code&
* &p&The tokens can be in &strong&any&/strong& order. Propagation and isolation codes
* must use the names of the constants in the TransactionDefinition class. Timeout values
* are in seconds. If no timeout is specified, the transaction manager will apply a default
* timeout specific to the particular transaction manager.
* &p&A "+" before an exception name substring indicates that
* transactions should commit even if this
* a "-" that they should roll back.
* @author Rod Johnson
* @author Juergen Hoeller
* @since 24.04.2003
* @see org.springframework.transaction.TransactionDefinition
* @see org.springframework.core.Constants
public class TransactionAttributeEditor extends PropertyEditorSupport {
* Format is PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2.
* Null or the empty string means that the method is non transactional.
* @see java.beans.PropertyEditor#setAsText(java.lang.String)
public void setAsText(String s) throws IllegalArgumentException {
if (s == null || "".equals(s)) {
setValue(null);
// tokenize it with ","
String[] tokens = maDelimitedListToStringArray(s);
RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
for (int i = 0; i & tokens. i++) {
String token = tokens[i].trim();
if (token.startsWith(RuleBasedTransactionAttribute.PREFIX_PROPAGATION)) {
attr.setPropagationBehaviorName(token);
else if (token.startsWith(RuleBasedTransactionAttribute.PREFIX_ISOLATION)) {
attr.setIsolationLevelName(token);
else if (token.startsWith(RuleBasedTransactionAttribute.PREFIX_TIMEOUT)) {
String value = token.substring(DefaultTransactionAttribute.PREFIX_TIMEOUT.length());
attr.setTimeout(Integer.parseInt(value));
else if (token.equals(RuleBasedTransactionAttribute.READ_ONLY_MARKER)) {
attr.setReadOnly(true);
else if (token.startsWith(RuleBasedTransactionAttribute.PREFIX_COMMIT_RULE)) {
attr.getRollbackRules().add(new NoRollbackRuleAttribute(token.substring(1)));
else if (token.startsWith(RuleBasedTransactionAttribute.PREFIX_ROLLBACK_RULE)) {
attr.getRollbackRules().add(new RollbackRuleAttribute(token.substring(1)));
throw new IllegalArgumentException("Illegal transaction attribute token: [" + token + "]");
setValue(attr);
从上面可以看出
token.substring() 这个方法把前缀timeout_给去掉了。。所以只剩下11了
从源码可看出来,PREFIX_XXXX
大都是这样写的,但前缀却是写在后面的。。觉得是不是命名有点古怪了,应该是XXXX_PREFIX,害我分析了一段时间。不过在源码上面的解释倒很清楚:
* PropertyEditor for TransactionAttribute objects. Takes Strings of form * &p&&code&PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2&/code& * &p&where only propagation code is required. For example: * &p&&code&PROPAGATION_MANDATORY,ISOLATION_DEFAULT&/code&
文笔不好。。就给出个实例,我想一看就该明白了。
&bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"&
事务拦截器bean需要依赖注入一个事务管理器 --&
&property name="transactionManager" ref="transactionManager"/&
&property name="transactionAttributes"&
下面定义事务传播属性--&
&prop key="get*"&PROPAGATION_REQUIRED,readOnly&/prop&
&prop key="*"&PROPAGATION_REQUIRED,timeout_11&/prop&
&/property&
总结先到这里。。等深入研究的时候再把写详细些。。。
事务隔离级别
数据库并发操作存在的异常情况:1. 更新丢失(Lost update):两个事务都同时更新一行数据但是第二个事务却中途失败退出导致对数据两个修改都失效了这是系统没有执行任何锁操作因此并发事务并没有被隔离开来。
2. 脏读取(Dirty Reads):一个事务开始读取了某行数据但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险很可能所有操作都被回滚。
3. 不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次但是却得到了不同结果。例如在两次读取中途有另外一个事务对该行数据进行了修改并提交。
4. 两次更新问题(Second lost updates problem):无法重复读取特例,有两个并发事务同时读取同一行数据然后其中一个对它进行修改提交而另一个也进行了修改提交这就会造成第一次写操作失效。
5. 幻读(Phantom Reads):也称为幻像(幻影)。事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的。
为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。
1.未授权读取(Read Uncommitted):也称未提交读。允许脏读取但不允许更新丢失,如果一个事务已经开始写数据则另外一个数据则不允许同时进行写操作但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。事务隔离的最低级别,仅可保证不读取物理损坏的数据。与READ COMMITTED 隔离级相反,它允许读取已经被其它用户修改但尚未提交确定的数据。
2. 授权读取(Read Committed):也称提交读。允许不可重复读取但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现,读取数据的事务允许其他事务继续访问该行数据,但是未提交写事务将会禁止其他事务访问该行。SQL Server 默认的级别。在此隔离级下,SELECT 命令不会返回尚未提交(Committed) 的数据,也不能返回脏数据。
3. 可重复读取(Repeatable Read):禁止不可重复读取和脏读取。但是有时可能出现幻影数据,这可以通过“共享读锁”和“排他写锁”实现,读取数据事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。在此隔离级下,用SELECT 命令读取的数据在整个命令执行过程中不会被更改。此选项会影响系统的效能,非必要情况最好不用此隔离级。
4. 串行(Serializable):也称可串行读。提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作事务访问到。事务隔离的最高级别,事务之间完全隔离。如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠事务均是串行的。
更新丢失 脏读取 重复读取 幻读 未授权读取
Y 授权读取
Y 可重复读取
浏览: 208803 次
来自: 广州
总结的很好,谢谢
我想问,是否支持获取method内的逻辑分支,比如if分支,普 ...
不错,不错哦
细致啊,楼主辛苦。
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'点击阅读原文
MySQL事务隔离级别和Spring事务关系介绍
7月17日 发布,来源:
事务隔离级别介绍
未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
接下来一次来验证每个隔离级别的特性,首先我们先建一张表,我们建立账户表account用来测试我们的事务隔离级别:
CREATE TABLE account (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_name` varchar(255) NOT NULL,
`money` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE `uniq_name` USING BTREE (customer_name)
) ENGINE=`InnoDB` AUTO_INCREMENT=10 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRIT
RU (read uncommitted)读未提交隔离级别
首先我们开启Console A,然后设置session事务隔离级别为 然后同样开启Console B,设置成
mysql& set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.03 sec)
mysql& select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED |
+------------------------+
1 rows in set (0.03 sec)
我们两个console的事务隔离级别都是read uncommitted,下面测试RU级别会发生的情况
可以发现RU模式下,一个事务可以读取到另一个未提交(commit)的数据,导致了脏读。如果B事务回滚了,就会造成数据的不一致。RU是事务隔离级别最低的。
RC (read committed)读提交隔离级别
现在我们将事务隔离级别设置成RC (read committed)
set session transaction isolation level read uncommitted;
我们在RC模式下,可以发现。在console B没有提交数据修改的commit的时候,console A是读不到修改后的数据的,这就避免了在RU模式中的脏读,但是有一个问题我们会发现,在console A同一个事务中。两次select的数据不一样,这就存在了不可重复读的问题.PS:RC事务隔离级别是Oracle数据库的默认隔离级别.
RR (Repeatable read)可重复读隔离级别
在RR级别中,我们解决了不可重复读的问题,即在这种隔离级别下,在一个事务中我们能够保证能够获取到一样的数据(即使已经有其他事务修改了我们的数据)。但是无法避免幻读,幻读简单的解释就是在数据有新增的时候,也无法保证两次得到的数据不一致,但是不同数据库对不同的RR级别有不同的实现,有时候或加上间隙锁来避免幻读。
innoDB 解决了幻读
前面的定义中RR级别是可能产生幻读,这是在传统的RR级别定义中会出现的。但是在innoDB引擎中利用MVCC多版本并发控制解决了这个问题
这算是幻读吗?在标准的RR隔离级别定义中是无法解决幻读问题的,比如我要保证可重复读,那么我们可以在我们的结果集的范围加一个锁(between 1 and 11),防止数据更改.但是我们毕竟不是锁住真个表,所以insert数据我们并不能保证他不插入。所以是有幻读的问题存在的。但是innodb引擎解决了幻读的问题,基于MVCC(多版本并发控制):在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。所以当我们执行update的时候,当前事务的版本号已经更新了?所以也算是幻读??(存疑)主要是gap间隙锁+MVCC解决幻读问题?
串行化隔离级别:
所有事物串行,最高隔离级别,性能最差
存在的问题?
在RR模型,我们虽然避免了幻读,但是存在一个问题,我们得到的数据不是数据中实时的数据,如果是对实时数据比较敏感的业务,这是不现实的。
对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。很显然,在MVCC中:
快照读:就是select
select * from table ….;
当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
select * from table where ?
select * from table where ?
事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了。
比如,我们有以下的订单业务场景,我们队一个商品下单的操作,我们得首先检查这个订单的数量还剩多少,然后下单。
select num from t_goods where id=1;
update t_goods set num=num-$mynum where id=1;
select num from t_goods where id=1;
update t_goods set num=num-$mynum where id=1;
假设这个时候数量只有1,我们下单也是只有1.如果在并发的情况下,事务1查询到还有一单准备下单,但是这个时候事务2已经提交了。订单变成0.这个事务1在执行update,就会造成事故。
解决问题方法1(悲观锁):就是利用for update对着个商品加锁,事务完成之后释放锁。切记where条件的有索引,否则会锁全表。
解决方法2(乐观锁):给数据库表加上个version字段。然后SQL改写:
select num,version from t_goods where id=1;
update t_goods set num=num-1,version=verison+1 where id=1 and version=${version}
Spring管理事务的方式
编程式事务
编程式事务就是利用手动代码编写事务相关的业务逻辑,这种方式比较复杂、啰嗦,但是更加灵活可控制(个人比较喜欢)
public void testTransactionTemplate() {
TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); //设置事务隔离级别
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//设置为required传播级别
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) { //事务块
jdbcTemplate.update(INSERT_SQL, "test");
声明式事务
为了避免我们每次都手动写代码,利用Spring AOP的方式对每个方法代理环绕,利用xml配置避免了写代码。
&tx:advice id="txAdvice" transaction-manager="txManager"&
&tx:attributes& &!--设置所有匹配的方法,然后设置传播级别和事务隔离--&
&tx:method name="save*" propagation="REQUIRED" /&
&tx:method name="add*" propagation="REQUIRED" /&
&tx:method name="create*" propagation="REQUIRED" /&
&tx:method name="insert*" propagation="REQUIRED" /&
&tx:method name="update*" propagation="REQUIRED" /&
&tx:method name="merge*" propagation="REQUIRED" /&
&tx:method name="del*" propagation="REQUIRED" /&
&tx:method name="remove*" propagation="REQUIRED" /&
&tx:method name="put*" propagation="REQUIRED" /&
&tx:method name="get*" propagation="SUPPORTS" read-only="true" /&
&tx:method name="count*" propagation="SUPPORTS" read-only="true" /&
&tx:method name="find*" propagation="SUPPORTS" read-only="true" /&
&tx:method name="list*" propagation="SUPPORTS" read-only="true" /&
&tx:method name="*" propagation="SUPPORTS" read-only="true" /&
&/tx:attributes&
&/tx:advice&
&aop:config&
&aop:pointcut id="txPointcut" expression="execution(* org.transaction..service.*.*(..))" /&
&aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /&
&/aop:config&
同时也可以用注解的方式
&tx:annotation-driven transaction-manager="transactioManager" /&&!--开启注解的方式--&
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;//传播级别
Isolation isolation() default Isolation.DEFAULT;//事务隔离级别
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;//事务超时时间
boolean readOnly()//只读事务
Class&? extends Throwable&[] rollbackFor() default {};//抛出哪些异常 会执行回滚
String[] rollbackForClassName() default {};
Class&? extends Throwable&[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};//不回滚的异常名称
//transaction注解可以放在方法上或者类上
我们在这里不对两种事务编程做过多的讲解
Spring事务传播:
事务传播行为:
Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为
为了演示事务传播行为,我们新建一张用户表
EATE TABLE user (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`pwd` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=`InnoDB` AUTO_INCREMENT=10 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;
Required:
必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务,如下图所示;
测试的代码如下,在account插入的地方主动回滚
public int insertAccount(final String customer, final int money) {
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//设置为required传播级别
int re= transactionTemplate.execute(new TransactionCallback&Integer&() {
public Integer doInTransaction( TransactionStatus status) {
int i = accountDao.insertAccount(customer, money);
status.setRollbackOnly();//主动回滚
return re;
public int inertUser(final String username, final String password) {
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//设置为required传播级别
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
int i = userDao.inertUser(username, password);
int hahha = accountService.insertAccount("hahha", 2222);
// status.setRollbackOnly();
System.out.println("user==="+i);
System.out.println("account===="+hahha);
按照required的逻辑,代码执行的逻辑如下:
在调用userService对象的insert方法时,此方法用的是Required传播行为且此时Spring事务管理器发现还没开启逻辑事务,因此Spring管理器觉得开启逻辑事务
在此逻辑事务中调用了accountService对象的insert方法,而在insert方法中发现同样用的是Required传播行为,因此直接使用该已经存在的逻辑事务;
返回userService,执行完并关闭事务
所以在这种情况下,两个事务属于同一个事务,一个回滚则两个任务都回滚。
RequiresNew:
创建新的逻辑事务,使用PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)如下图所示:
Supports:
支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行,如下图所示:
NotSupported:
不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行。
Mandatory:
必须有事务,否则抛出异常,使用PROPAGATION_MANDATORY指定,使用当前事务执行,如果当前没有事务,则抛出异常(IllegalTransactionStateException)。当运行在存在逻辑事务中则以当前事务运行,如果没有运行在事务中,则抛出异常
不支持事务,如果当前存在是事务则抛出异常,使用PROPAGATION_NEVER指定,即以非事务方式执行,如果当前存在事务,则抛出异常(IllegalTransactionStateException)
嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚。
Nested和RequiresNew的区别:
RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;
Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;
Nested使用JDBC 3的保存点(save point)实现,即如果使用低版本驱动将导致不支持嵌套事务。
使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要设置来开启。
我要该,理由是:
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)

我要回帖

更多关于 spring 事务隔离级别 的文章

 

随机推荐