MySQL 的事务
1 什么是事务
将一组操作封装在一起,要么全部成功、要么全部失败。当在一个事务中执行多个操作时、要么所有事务都被提交(commit),那么这些修改就永久保存下来;要么数据库管理系统将放弃所有修改,整个事务回滚(rollback)到最初的状态。MySQL中只有InnoDB引擎支持事务的操作
2 事务的特性
事务由四大特性(ACID),分别是原子性、持久性、一致性、隔离性
原子性(Atomicity)
⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过⼀样。
持久性(Consistency)
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。持久性时通过
事务日志
来保证的。日志包括了重做日志
和回滚日志
。一致性(Durability)
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的工作。
隔离性(Isolation)
数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防⽌多个事务并发执行时由于交叉执行而导致数据的不⼀致。
3 事务的隔离级别
READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间 的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,比如此级别的事务正在执行时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,自己重复插⼊时又失败 (因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但自己就是插⼊不进去,这就叫幻读 (Phantom Read)。
SERIALIZABLE:序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
事务的隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(READ UNCOMMITTED) | √ | √ | √ |
读已提交(READ COMMITTED) | × | √ | √ |
可重复读(PEREATABLE READ) | × | × | √ |
序列化(SERIALIZABLE) | × | × | × |
3.1 查看当前数据库隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
# 或者SELECT @@transaction_lation;

3.2 设置数据隔离级别
3.2.1 通过命令行设置(数据库重启后失效)
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL READ COMMITTED;
# 或者 SET [GLOBAL|SESSION] transaction_isolation = read_commitred;
3.2.2 通过配置文件设置
如果在服务器启动时想改变事务的默认隔离级别,可以修改启动参数transaction_isolation的值。比如,在启动服务器时指定了 transaction_isolation=SERIALIZABLE,那么事务的默认隔离级别就从原来的REPEATABLE-READ变成了SERIALIZABLE
4 MySQL 事务日志
事务由4中特效:原子性、一致性、隔离性、持久性。那么事务的四种特性时基于什么机制实现的呢?
- 事务的隔离性由
锁机制
实现 - 事务的原子性、隔离性、持久性由事务的redo日志和undo日志来保证
- REDO LOG 重做日志。提供写入操作,恢复提交事务修改的页操作,用来保证事务的持久性
- UNDO LOG 回滚日志,回滚行记录到某个特定的版本,用来保证事务的原子性、一致性
REDO和UNDO都可以视为一种恢复操作,但是:
- REDO LOG :是存储引擎层生成的日志、记录的是
物理基本
上的页修改操作。比如页号xxx、偏移量yyy写入了zzz的数据,主要为了保证数据的可靠性 - UNDO LOG:也是存储引擎生成的日志、记录的是
逻辑操作
日志,比如对某一行数据进行了INSERT语句操作,那么UNDO LOG就记录一条与之相反的DELETE操作。主要用于事务的回滚
(UNDO LOG记录的是每个操作的逆向操作
)
5 Spring 事务的传播机制
REQUIRED
默认的事务级别,当前存在事务,则加入事务;没有则创建一个事务
SUPPORTS
当前有事务则加入,没有事务则以非事务的方式运行
MANDATORY
如果当前存在事务,加入事务;没有事务则抛出异常
REQUIRED_NEW
创建一个新的事务,如果当前存在事务,则将当前事务挂起。
NOT_SUPPORTED
以非事务的方式运行,如果当前存在事务,则将当前事务挂起
NEVER
以非事务的方式运行,如果当前存在事务,抛出异常
NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则相当于REQUIRED
6 Spring 事务的实现
6.1 编程式事务
- SpringBoot 内置的
DataSourceTransactionManager
用来开启事务、提交和回滚 TransactionDefinition
用来配置事务的属性- 开启事务时,将TransactionDefinition传入,获得一个事务的TransactionStatus
@Autowired
DataSourceTransactionManager transactionManager;
public Object testTransaction() {
// 创建事务的属性配置对象
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 配置事务的隔离级别
definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 配置事务的传播机制
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//开启事务
TransactionStatus transaction = transactionManager.getTransaction(definition);
try {
// 此处进行数据库的操作
// 正常执行完成,提交事务
transactionManager.commit(transaction);
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(transaction);
throw new RuntimeException(e);
}
return "SUCCESS";
}
6.2 声明式事务
通过在方法上加@Transaction注解来开启事务,不必再手动进行事务的提交和回滚,方法正常执行完成会提交事务,方法抛出RuntimeException时会回滚事务。
@Transaction
public void transactionMethod(){
// 数据库的操作
}
设置事务的回滚拦截的异常
// 通过rollbackFor 来指定触发事务回滚的异常类型 @Transactional(rollbackFor = Exception.class)
设置事务的隔离级别
// isolation 指定事务的隔离级别 @Transactional(isolation = Isolation.READ_COMMITTED)
设置事务的传播机制
// propagation 指定事务的传播机制 @Transactional(propagation = Propagation.REQUIRED)
注意事项:@Transaction默认触发事务回滚的异常是RuntimeException
6.3 Spring 事务失效的情况
- 配置未开启事务(没有配置DataSourceTransactionManager)
- 抛出的异常并非拦截的异常
- @Transaction注解所在的方法是private的,代理类无法重写
- 没有调用代理类的方法,则spring是不会进行事务的处理