1. 引言
我们常听到事务隔离级别这个概念,而又分spring事务隔离级别和数据库事务隔离级别,那么它们分别是什么?并且有什么关系呢?
2. spring隔离级别
常量 | 解释 |
ISOLATION_DEFAULT | 这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。 |
ISOLATION_READ_UNCOMMITTED | 事务中的修改,即使没有提交,其他事务也可以看得到,会导致“脏读”、“幻读”和“不可重复读取” |
ISOLATION_READ_COMMITTED | 大多数主流数据库的默认事务等级,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”,SQL Server和Oracle数据库默认的隔离级别 |
ISOLATION_REPEATABLE_READ | 避免了“脏读取”和“不可重复读取”的情况,但不能避免“幻读” ,Mysql数据库默认的隔离级别 |
ISOLATION_SERIALIZABLE | 事务被处理为串行化顺序执行,代价最大,很少用 |
spring事务:
- spring事务实际上是使用spring的一大特性AOP拦截注解的方法,动态代理处理异常情况;
- spring事务只有捕获到异常才会终止或回滚,如果你在程序中try/catch后自己处理异常而没有throw,那么事务将不会起作用(终止或回滚);
- spring事务会捕获所有的异常,但只会回滚数据库相关的操作,并且只有在声明了rollbackForClassName="Exception"之类的配置才会回滚;
- spring事务会回滚同一事务中的所有数据库操作,本质上是回滚同一数据库连接上的数据库操作;
使用spring难免要用到spring的事务管理,要用事务管理又会很自然的选择声明式的事务管理;
spring声明式事务管理:
spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。
那么什么是检查型异常什么又是非检查型异常呢?
最简单的判断点有两个:
- 继承自runtimeexception或error的是非检查型异常,其余的是检查异常
- 对非检查型类异常可以不用捕获,而检查型异常则必须用try{}catch{}语句块进行处理或把异常交给上级方法处理,总之就是必须写代码处理它。所以必须在service捕获异常,然后再次抛出,这样事务方才起效
即异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。
@Transactional(rollbackFor=Exception.class) //默认只有运行时异常才事务回滚
@Transactional(notRollbackFor=RunTimeException.class) //非运行时异常才事务回滚
@Transactional(propagation=Propagation.NOT_SUPPORTED) //不需要事务管理的(只查询的)方法
3. 数据库隔离级别
隔离级别 | 隔离级别的值 | 导致的问题 |
Read-Uncommitted | 0 | 导致脏读 |
Read-Committed | 1 | 避免脏读,允许不可重复读和幻读 |
Repeatable-Read | 2 | 避免脏读,不可重复读,允许幻读 |
Serializable | 3 | 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重 |
- 为什么会出现“脏读”?
因为没有“select”操作没有规矩。 - 为什么会出现“不可重复读”?
因为“update”操作没有规矩。 - 为什么会出现“幻读”?
因为“insert”和“delete”操作没有规矩。
4. spring隔离级别&数据库隔离级别的关系
spring默认使用的隔离级别是ISOLATION_DEFAULT,随数据库默认的隔离级别;而spring事务本质上使用数据库事务,而数据库事务本质上使用数据库锁,所以spring事务本质上使用数据库锁,开启spring事务意味着使用数据库锁;
5. 怎么解决避免不了幻读的问题?
- 既然主流数据库在隔离级别上都避免不了幻读,那么这个问题是怎么解决的呢?
通过MVCC(multi version concurrent control)多版本并发控制机制来控制的,mysql数据库上其实每条上面都还有两个冗余字段,一个是行的创建版本,一个是行的删除(过期)版本。具体的版本号(trx_id)存在 information_schema.INNODB_TRX 表中。版本号(trx_id)随着每次事务的开启自增。
原理 :将历史数据存一份快照,所以其他事务增加与删除数据,对于当前事务来说是不可见的。