Spring事务概述 本地事务(区分于分布式事务)也叫数据库事务,MySql的InnoDB引擎已经支持事务,Spring中的事务实在底层数据库事务的基础上进一步封装,可以在不同的项目、不同的操作中对事务的传播行为和隔离级别做细粒度的控制
1.1 事务管理方式
支持两种:编程式事务和声明式事务
1.1.1 编程式事务
推荐使用 TransactionTemplate,在需要开启事务的代码处,通过类似数据库开启BRGIN、COMMIT这种需要进行编码开启事务和提交事务或者回滚,即需要在代码中显示地进行编码,从而实现事务的效果 实际开发中编程式事务很少使用。
1.1.2 声明式事务
Spring声明式事务基于AOP,本质是对需要开启事务的地方通过aop织入的方式进行拦截,即在执行方法前创建或者加入一个事务,执行完方法后根据执行情况提交/回滚事务。实际开发中通过注解@Transcation修饰开启事务的方法
1.1.3 对比
优点:非侵入式开发。声明式事务不需要对业务代码进行事务相关编码这种侵入式的开发,仅需要注解即可,使业务代码不受污染。
缺点:粗粒度。声明式事务的粒度只能在方法级别上,而编程式事务可以作用到代码块级别。
1.2 事务的ACID特性
原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功和失败),系统须确保它所建模的业务处于一致的状态,而不是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation):许多事务处理相同的数据时,事务应该与气压事务隔离开来,防止数据损坏。换句话说就是事务并发的问题,下沉到数据库就是同一时刻有多个线程对同一张表或者同一行数据进行写操作,数据库中需要加行锁/表锁/乐观锁保证事务的隔离性
持久性(Durability):一旦事务提交,那么它对数据库中的数据状态的变成就会永久保存到数据库,即发生宕机后,数据库重新启动后,数据就行恢复到宕机结束时的状态。 关于原子性和一致性的区分:C是最终达到的目的,AID是达到目的的手段
1.3 事务的并发问题
同一时刻有多个事务同时对数据库进行操作(尤其是写操作),如果这些操作作用在同一张表甚至同一行数据,会带来如下事务并发问题(从问题严重性从大到小排序):
脏写(Dirty Write): 事务A先更新某行数据,事务B后更新该行数据,此时如果事务A回滚了,那么该行数据的值就会回滚成功事务A更新前的数据值,事务B会发现自己更新的数据不见了,这就是脏写;
脏读(Dirty Read): 脏读就是一个事务读取了另一个事务修改后为提交的数据,此时如果修改的事务回滚了,那么第一个事务相当于读到了一个不存在的数据,这就是脏读;
不可重复度(Non-Repeatable Read): 事务A对一条记录不断的查询,事务B对这条记录不断的UPDATE,导致每次事务A查询时得到的记录都是不一样的,就造成了不可重复读;
幻读(Phantom): 事务A对一条记录不断的查询,事务B同时向这张表里INSERT符合事务A查询条件的记录,导致每次事务A按照相同的条件查询时得到的记录数目都是不一样的,就造成了幻读。 幻读与不可重复度的区别是:不可重复读的结果强调每次读取时的记录内容不同,幻读的结果强调每次读取记录时都读取到了之前没读取到的记录,即记录数在递增。
1.4 事务的隔离级别
事务的隔离级别定义一个事务可以接受其他事务并发活动影响的程度,与Java里锁的机制类似,当有多个事务同时访问(尤其是并发写的情况)一份记录时,应该通过某些机制让这些事务串行执行以此保证事务的隔离性,但服务器又想同一时刻尽可能多地处理访问请求,因此数据库设立了不同的隔离级别,牺牲部分隔离性而获得性能上的提升。
sql标准中有四种隔离级别,如下:
READ UNCOMMITTED :未提交读;
READ COMMITTED: 已提交读;
REPEATABLE READ :可重复读;
SERIALIZABLE: 可串行化。
前面提到过为了追求性能,SQL会牺牲一部分并发安全性,SQL标准中规定,针对不同的隔离级别,可以允许不同程度严重程度的并发事务问题发生,具体如下:
隔离级别 | 脏读 | 不可重复度 | 幻读 |
READ UNCOMMITTED | 允许 | 允许 | 允许 |
READ COMMITTED | 不允许 | 允许 | 允许 |
REPEATABLE READ | 不允许 | 不允许 | 允许 |
SERIALIZABLE | 不允许 | 不允许 | 不允许 |
说明:
由于脏写问题太严重,上面所有隔离级别都不会允许脏写发生; MySQL的InnoDB引擎默认的隔离级别是REPEATABLE READ。
















