Mysql数据库事务
1、事务概述
1.1、存储引擎支持情况
目前只有InnoDB支持事务
1.2、事务的基本概念
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
- 事务的处理原则:保证所有事务都作为 一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交( commit ),永久地保存下来;要么数据库管理系统将放弃所有修改 ,整个事务回滚( rollback )到最初状态。
1.3、事务的ACID特性
- 原子性 原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。
- 一致性 一致性是指事务执行前后,数据从一个合法性状态变换到另外一个合法性状态。这种状态是语义上的而不是语法上的,跟具体的业务有关。
- 隔离性 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
1.4、事务的状态
- 活动的 事务对应的数据库操作正在执行过程中时,我们就说该事务处在
活动
状态。 - 部分提交的 当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并
没有刷新到磁盘
时,我们就说该事务处在部分提交的
状态。 - 失败的 当事务处在
活动的
或者没有刷新到磁盘部分提交的
状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败
的状态。 - 中止的 如果事务执行了一部分而变为
失败的
状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称之为回滚
。当回滚
操作完成后,也就是数据库恢复到执行事务之前的状态,我们就说该事务处在了中止的
状态 - 提交的 当一个处在
部分提交的
状态的事务将修改过的数据都同步到磁盘
上之后,我们就可以说该事务处在了提交的
状态。
2、使用事务
2.1、显式事务
start transaction # 或者 begin
start transaction
对比 begin
可以在后面添加一些修饰符
- ①
read only
表示是一个只读事务 - ②
read write
表示当前事务是一个读写事务 - ③
with consistent snapshot
启动一致性读写
commit # 提交事务
rollback # 回滚事务
rollback to [saveport] # 回滚到保存点
# 注意:此时事务还没有结束 要表示一个事务结束要么是 commit 要么是 rollback 我们的事务状态只有提交的、中止的,没有回滚到保存点的
2.2、隐式事务
Mysql中有变量 autocommit
也就是说这个变量是开启状态,那么隐式事务就是一个开启的状态,有哪些情况是隐式事务在发挥作用的结果呢
- 数据定义语言(DDL)
- 隐式使用或修改mysql数据库的表
- 事务控制或者关于锁定的语句
① 当我们一个事务还没提交或者回滚时又使用start transaction
或者begin
语句开启另一个事务那么会隐式提交上一个事务
② 当autocommit
系统变量的值为OFF
我们手动调为ON
时也会隐式提交前句所属的事务
③ 使用LOCK TABLES
、UNLOCK TABLES
等关于锁的语句也会隐式提交
前句所属的事务 - 加载数据的语句
- 关于Msql复制的语句
- 其他一些语句
3、事务的隔离级别
3.1、数据并发问题
- 脏写(Dirty Write) :对于两个事务 sessionA 、sessionB ,如果sessionA
修改了
另一个未提交
的sessionB修改过的数据,那就意味着发生了脏写
- 脏读(Dirty Reda) :对于两个事务 sessionA 、sessionB ,如果sessionA
读取到了
另一个未提交
的sessionB修改过的数据,那就意味着发生了脏读
,因为此时读取到的数据是不可靠的是 sessionB 事务下的临时数据,并没有持久化到数据库中 - 不可重复读(Non-Repeatable Read) :对于两个事务 sessionA、sessionB ,sessionA
读取
了一个字段,然后sessionB更改
了改字段。之后sessionA又读取了改字段发现值不同了,那就意味着发生了不可重复读
- 幻读(Phantom) :对于两个事务sessionA、sessionB,sessionA从表中
读取
数据,然后sessionB在该表中插入了一些新的行,之后如果sessionA再次读取
同一张表发现多了几行,那就意味着发生了幻读
3.2、SQL标准中的四种隔离级别
3.1介绍了并发事务在执行过程中可能遇到的问题,这些问题有些比较严重,我们按照轻重缓急排序一下
脏写 > 脏读 > 不可重复读 > 幻读
舍弃了一部分隔离性来换取一部分性能,这里就体现在隔离级别上:隔离级别越低,并发问题发生的也就比较多
- READ_UNCOMMITED:读未提交,所有事务都可以看到其他未提交的事务执行结果。不能避免脏读、不可重复读、幻读
- READ_COMMITED:读已提交,她满足了隔离的简单定义:一个事务只能看见已经提交事务做的改变。这是大多数数据库系统的默认隔离级别(Mysql不是)。可以避免脏读,但不可重复读、幻读依然存在
- REPEATABLE READ:可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别。
- SERIALIZABLE: 可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。
3.3、MySQL支持的四种隔离级别
MySQL 默认的隔离级别是REPEATABLE READ
我们可以手动修改事务的隔离级别
# 查看隔离级别 MySQL 5.7.20以前
show variables like 'tx_isolation'
# MySQL 5.7.20 之后引入了 transaction_isolation 来替换 tx_isolation
show variables like 'transaction_isolation'
# 任何版本都可以使用以下语句
select @@transaction_isolation;
3.4、如何设置事务的隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
#其中,隔离级别格式:
> READ UNCOMMITTED
> READ COMMITTED
> REPEATABLE READ
> SERIALIZABLE
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
#其中,隔离级别格式:
> READ-UNCOMMITTED
> READ-COMMITTED
> REPEATABLE-READ
> SERIALIZABLE
设置时使用GLOBAL或者SESSION的影响
- 使用
GLOBAL
关键字(在全局范围影响)
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET GLOBAL TRANSACTION_ISOLATION = 'SERIALIZABLE';
当前已经存在的会话无效
只对执行完该语句之后产生的会话起作用
- 使用
SESSION
关键字(在会话范围内影响)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET SESSION TRANSACTION_ISOLATION = 'SERIALIZABLE';
对当前会话的所有后续的事务有效
如果在事务之间执行,则对后续的事务有效
该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务
4、Spring中的事务管理
4.1、Spring中的事务抽象
通过上图可以看到Spring中的事务抽象主要有三个,分别是
PlatFormTransactionMangeer
:事务管理器 (用来管理事务,包含事务的提交、回滚)TransactionDefinition
:事务定义信息(隔离,传播,超时,只读)TransactionStatus
:事务具体运行状态
4.2、事务抽象详解
4.2.1、PlatformTransactionManager核心事务管理器
public interface PlatformTransactionManager extends TransactionManager {
// 获取事务状态信息
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
从上图可以看到PlatformTransactionManager主要有四种实现类
- DataSourceTransactionManager 用于JDBC的事务管理器
- HibernateTransactionManger 用于Hibernate的事务管理器
- JpaTransactionManager 用于JPA的事务管理
- JtaTransactionManager 用于java原生的事务管理
我们引入不同的依赖,会使用不同的事务管理器
4.2.2、TrainsactionDefinition信息对象
public interface TransactionDefinition {
// 返回事务的传播行为
int getPropagationBehavior();
// 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getIsolationLevel();
// 返回事务必须在多少秒内完成
int getTimeout();
// 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
boolean isReadOnly();
}
4.2.3、TransactionStatus运行状态
public interface TransactionStatus{
// 是否是新的事务
boolean isNewTransaction();
// 是否有保存点
boolean hasSavepoint();
// 设置为只回滚
void setRollbackOnly();
// 是否为只回滚
boolean isRollbackOnly();
// 是否已完成
boolean isCompleted;
}
4.3、Spring中的声明式事务
声明式事务主要就是 @Transactional
注解
- 如果使用xml需要在xml文件中配置事务信息
- 如果使用注解则需要使用
@EnableTransactionManagement
开启事务管理功能
<tx:annotation-driven />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
- 然后就是将
@Transactional
注解加入到合适的方法上去
4.3.1、@Transactional注解信息
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* 在配置文件中有多个 TransactionManager,可以使用该属性指定选择哪个事务管理器
*/
@AliasFor("transactionManager")
String value() default "";
/**
* 在配置文件中有多个 TransactionManager,可以使用该属性指定选择哪个事务管理器
*/
@AliasFor("value")
String transactionManager() default "";
/**
* 事务的传播行为,默认值REQUEIRED
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* 事务的隔离度,默认值采用DEFAULT
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* 事务的超时时间默认值 -1 ,如果超过该时间限制但事务还没有完成,则自动回滚事务
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* 指定事务是否为只读事务,默认值false,为了忽略哪些不需要事务的方法,比如读取数据,可以设置为true
*/
boolean readOnly() default false;
/**
* 用于指定能够触发事务鬼滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过 , 分隔
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* 同上,使用全类名字符串
*/
String[] rollbackForClassName() default {};
/**
* 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过 , 分隔
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* 同上,使用全类名字符串
*/
String[] noRollbackForClassName() default {};
}