Spring-白话事务
什么是事务,把一组逻辑放在一起作为一个单元来提交执行,这就是事物,这不是定义,大概是这么个意思
如果你留心的话,你会看到到处都有事物,到处都会提到ACID四个特性(原子性,一致性,隔离性,持久性)
Redis的设计,Mysql的设计,Spring的设计,许许多多都有,并且也都是围绕着这4个特性展开,简单看下:
原子性(atomicity):要么一起成功,要么一起失败,通俗点你可以把他们看成一句代码在执行。。
一致性(consistency):执行前后数据的完整性保持一致
隔离性(isolation):正在执行的这个事物不会受到隔壁事物的干扰
持久性(durability):事物结束,数据就持久到数据库
以上Redis和Mysql的InnoDB存储引擎完美的支持事物(但是注意Mysql的Myisam引擎是不支持的,Redis支持事物,
但并不支持回滚)
由于Spring的事物支持和Mysql息息相关,下面我们关注下Mysql事物的几种隔离级别:
我们先来一个RR,这是使用最多的,也是Mysql的默认设置,RR就是Repeatable Read,可重复读,也就是多次读取结果
一致,什么情况会不一致呢,read uncommitted,望文生义就是读到未提交的,也就是脏读(程序中的说法),Mysql叫
未提交读,什么时候会出现脏读?现在开发一般很少出现,但是,但是,笔者曾经制造过一次,很久才反应过来,这真的
是人为制造的,那个需求是更新同一条记录的count字段+1造成的,这个接口的逻辑有点长,先select读取这条记录,一系
列的判断后决定是否update count加1,最终在某些情况下造成了一个死锁。测试并发情况下还是必现的。有兴趣的可以打开
两个mysql命令行窗口模拟下,首先将autocommited设置为0,不自动提交事务,然后窗口1下执行select记录A,窗口2下执行
select记录A,窗口1接着执行update A,窗口B执行update A,这个时候很关键了,窗口1事物commit,窗口2事物commit
接着你会收到一个超时的死锁提示,这个不细讲了。这里其实出现了一个问题,窗口2的select应该在窗口1update执行完毕之
后才能执行,这才能保证读到的数据不是在脏数据。至于read commited,提交读,很明显,可以避免脏读,但是接着再重复读
的话和先前的结果就不一致了,所以无法避免重复读的问题,最后一个serializable最霸气,序列化,大家谁也别抢,谁也别争
一个个来,解决了脏读,重复读等一系列问题,它叫序列化,和很多设计一样,优势带来的必然就有劣势,这个劣势很明显,
看名字序列化,效率很低下。
白话了这么多,首先告诉你,Spring的默认事物隔离级别跟数据库是保持一致的。接着看
在Spring中如果不考虑隔离级别,我们可能遇到下面几种情况
读到另一事物未提交的数据
读到另一事物已提交的insert的数据,导致多次查询结果不一致
读到另一事务已经提交的 update 的数据导致多次查询结果不一致
我们分别叫他们脏读,幻读,不可重复读
为了解决读这个问题,Spring设置了5种事物隔离级别
对了你没看错,上面那一长段介绍的mysql的4种情况,加上一个默认情况(数据库默认隔离级别),对应的参数名就是isolation
对应可取参数值为TransactionDefinition.ISOLATION_DEFAULT,TransactionDefinition.ISOLATION_REPEATABLE_READ,其他
的几个参数值类似,不再列举
最后我们看下7种传播行为
事物的传播行为描述的是当某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
给一个直观的代码片段:
public void methodA(){ methodB(); //doSomething } @Transaction(Propagation=XXX) public void methodB(){ //doSomething }
传播行为在事物中占比较重的地位,主要是定义了外围方法在开启事物和未开启事务2种情况下嵌套内
部事物时,关于异常回滚的规则,因此笔者准备开一篇新的随笔贴出实践的代码和分析结果,这里在分
析业务的时候一定注意这么几点:
1、谁调用谁?比如方法a()中调用方法b()
2、a是否在一个开启的事物中
3、a和b抛出异常时,业务要求如何回滚?
4、按照Spring的事物传播行为来配置,测试是否满足对应业务场景