事务 ( transaction )

事务 ( transaction )

事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消. 即要么都完成,要么都失败.

事务的四大特性 ( ACID )

原子性 ( Atomicity )

一个事务 ( transaction ) 中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节. 事务在执行过程中发生错误,会被回滚 ( Rollback ) 到事务开始前的状态,就像这个事务从来没有执行过一样. 即,事务不可分割、不可约简.

一致性 ( Consistency )

在事务开始之前和事务结束以后,数据库的完整性没有被破坏. 这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等.

隔离性 ( lsolation )

数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致. 事务隔离分为不同级别,包括 读未提交 ( Read Uncommitted )、读已提交 ( Read Committed )、可重复读 ( Repeatable Read ) 和 串行化 ( Serializable ).

持久性 ( Durability )

事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失.

只有保证了事务的 持久性原子性隔离性 之后,一致性 才能得到保障. 也就是说 A、I、D 是手段,C 是目的!

事务并发引起的问题

脏读

一个事务会读取到另一个事务未提交的数据.

不可重复读

在同一事务内,事务两次读取到的数据不一样的.

幻读

事务中的同一个查询在不同的时间产生不同的行集,这个就是幻读问题.

  1. 不可重复读和幻读,都是读取到其他事务已经提交的数据. 而脏读是读取到其他事务还未提交的数据.
  2. 二者描述的则重点不同,不可重复读描述的侧重点是 update,而幻读描述的侧重点是 insertdelete.

Read Uncommitted ( 读未提交 )

一个事务还没提交时,它做的变更就能被别的事务看到.

Read Committed ( 读已提交 )

一个事务提交之后,它做的变更才会被其他事务看到.

Repeatable Read ( 可重复读 )

一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的. 这是MySQL InnoDB 引擎的默认隔离级别.

Serializable ( 串行化 )

对于同一行记录,"写" 会加 "写锁","读" 会加 "读锁". 当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行.

事务不同隔离级别导致的脏读、不可重复读和幻读

事务隔离级别 脏读 不可重复读 幻读
读未提交 ( READ UNCOMMITTED )
读已提交 ( READ COMMITTED ) ×
可重复读 ( REPEATABLE READ ) × ×
串行化 ( SERIALIZABLE ) × × ×

spring 中的事务

spring 支持两种事务,一个是编程式事务管理,一个是声明式事务管理. ( 两者可混合 )

编程式事务管理 ( 不推荐 )

侵入性太强,不推荐. 不如 AOP 优美、简洁.

声明式事务管理 ( 推荐 )

对于声明式事务只需要在方法和类上添加 @Transactional 注解即可.

  1. 该注解只对 public 类和方法有效. 加在类上对所有 public 方法有效.
  2. 方法前会自动开启事务,如果方法没有抛出异常,执行后会自动提交事务,否则发生回滚.
  3. 只能回滚非检查型异常,具体为 RuntimeException 及其子类和 Error 子类,可以从Spring源码的 DefaultTransactionAttribute 类里找到判断方法 rollbackOn.
1
2
3
4
@Override
public boolean rollbackOn(Throwable e) {
return (e instanceof RuntimeException || e instanceof Error);
}
  1. 不能回滚被 try{}catch() 捕获的异常.

解决方案

  • catch 里抛出异常,让 AOP 捕获并回滚.
  • catch 里 添加 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 手动回滚.
  1. 无事务的方法调用有事务的方法会导致方法失效.

@Transactional 属性

propagation ( 传播性 )

枚举类型 Propagation 默认 Propagation.REQUIRED. ( 默认基本够用,顶多 REQUIRES_NEWNESTED )

REQUIRED

支持当前事务,如果当前没有事务,则创建一个事务,这是最常见的选择.

SUPPORTS

支持当前事务,如果当前没有事务,就以非事务来执行.

MANDATORY

支持当前事务,如果没有当前事务,就抛出异常.

REQUIRES_NEW

开启一个新的事务. 如果一个事务已经存在,则先将这个存在的事务挂起.

NOT_SUPPORTED

总是非事务地执行,并挂起任何存在的事务.

NEVER

总是非事务地执行,不加入任何事务.

NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按 REQUIRED 属性执行.

isolation ( 隔离性 )

参照上面 Mysql 的事务隔离. 默认为 READ_COMMITTED.

readOnly ( 只读性 )

用在查询 ( select ) 上,一条查询执行多次时候用,防止查询出来的数据不一致.

timeout ( 超时 )

超时自动回滚. 单位秒

rollbackFor ( 回滚异常类 )

用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.

rollbackForClassName ( 回滚异常类名 )

和上面一个意思,只不过是类型变成类名.

noRollbackFor ( 不回滚异常类 )

抛出指定的异常类型,不回滚事务,也可以指定多个异常类型.

noRollbackForClassname ( 不回滚异常类名 )

和上面一个意思,只不过是类型变成类名.