1. 事务介绍
通俗的说,所谓事务就是操作一组数据库操作,要么都成功,要么都失败。
事务有四种特性(ACID):
- 原子性(atomicity),强调事务是一个不可分割的整体,要么都执行成功,要么都执行失败;
- 一致性(consistency),事务执行前后保证数据的完整性,比如A向B转账,不存在A扣了钱,B还未收到帐;
- 隔离性(isolation),当事务操作某一数据时,不允许其他事务来干扰,比如A取钱的时候,不允许B给A转账;
- 持久性(durability),事务一旦结束,数据就会持久到数据库中,也就是说,一旦提交(commit)后,是不可以回退(rollback)的。
2. 事务问题
那么使用事务的过程中会遇到哪些问题呢?
主要有三种:脏读、不可重复读和幻读。
2.1 脏读
定义:一个事务读取另一个发生失败和回滚的事务中未提交的数据;
举例:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
2.2 不可重复读
定义:一个事务在读某一行数据时,另一个事务也在更新这条数据,然后发现多次查询结果不一致;
举例:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
2.3 幻读
定义:一个事务在读取多行数据时,另一个事务也在处理这些数据,然后发现多次查询返回的行数不一致;
举例:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
3. 事务隔离
事务隔离便是解决多个事务之间的脏读、不可重复读和幻读问题。
事务隔离有四种:读未提交、读已提交、可重复读和序列化。
- 读未提交(read-uncommitted):级别最低,脏读、不可重复读和幻读都有可能发生;
- 读已提交(read-committed):可避免脏读,不可避免不可重复读和幻读;
- 可重复读:可避免脏读和不可重复读,不可避免幻读;
- 串行化:级别最高,表示都可避免;
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
数据库事务隔离对比:
- Oracle 只支持 读已提交(read-committed)和串行化(serializable),默认是读已提交;
- MySQL 读未提交、读已提交、可重复读和序列化这四种都支持,默认可重复读(repeatable-read)。
在 Spring 中默认使用数据库的隔离级别。
4. 事务传播
在 Spring 中定义了有七种事务传播行为,用来处理一个事务方法被另一个事务方法调用时该如何进行。
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
注意:
- 事务传播行为针对的被调用的事务方法;
- 上面所说的当前事务,是指的是外层方法(调用者);