定义:事务是一个最小的不可再分的工作单元;一个事务代表一个完整的业务操作,作为单个逻辑工作单元执行的一些列操作,要么完全执行,要么完全不执行。
例如:银行转账,A账户转给B账户一百万,首先A账户上要减少这100w,然后B账户上要增加100w,这样才算是整个转账业务完全完成,如果A账号减少了,B账户上没增加,这肯定是不行的,或者说A账户没减少,B账户上增加了,那也肯定是不行的。所以要么就两个账户都正常增加减少,要么就都不变。
大家发现,其实事务最终还是在数据库的层面,像银行转账,我们可以把它看成一个完整的业务操作,假如最开始AB账户上都有100w,当A转账给B钱时,请求到后台的转账方法上,经过一系列逻辑操作到数据库层会先执行两条sql:update bill set money = 0 where account = A;以及update bill set money = 2000000 where account = B;这两条sql的执行就必须在同一个事务中进行,不然就有可能出现一条sql执行成功,另一条sql执行失败的情况。这样结果银行肯定是不想看到的。
说到事务,最开始已经提到它是一个不可再分的工作单元。那么事务到底还有哪些具体的特性呢?
二、事务的特性事务四大特性:
-
原子性:
事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出现以下两种状态之一:
- 全部成功执行。
- 全部不执行。
-
一致性:
事务要求所有的DML语句操作时,必须保证同时成功或者失败;一个事务在执行之前和执行之后,数据库都必须处于一致性状态。也就是说事务执行的结果必须是使数据库从一个一致性状态转变到另一个一致性状态
-
隔离性:
事务A和事务B之间具有隔离性,也就是一个事务的执行不能被其他事务所干扰;不同的事务并发操纵相同的数据时,每个事务都有各自完整的数据空间,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的 各个事务之间不能互相干扰。
-
持久性:
一个事务一旦提交,它对数据库中对应数据的状态变更就应该是永久性的;某个事务成功结束,那么它对数据库所做的更新就必须被永久保存下来——即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束时的状态。
了解了事务的四大特性,这里我们在介绍事务的隔离性之前,先看一下,在MySQL中事务是如何开启和提交的。并且在一个事务提交之前,其他事务如果也操作了这条数据会发生什么情况?
三、mysql手动开启和提交事务-
首先创建一张bill账单表
-
打开查询框,输入命令 BEGIN; 或者 START TRANSACTION; 表示事务开启;
-
update更新账户A的的金额,此时还没有提交事务,再查询账户A的金额,在此查询中会发现账户A金额已经减少。
-
我们重新打开一个查询框去查询账户A的金额,会发现账户A金额还是100w;(重新打开一个查询,就相当于另一个线程去操作数据了)
-
然后输入COMMIT;提交事务。(这里也可以输入ROLLBACK进行事务回滚)
-
新打开的查询框在去查询账户A会发现金额已经为0。(如果是事务回滚,则查询到的A金额则还是100w)
我们知道了事务具有隔离性,那么事务是如果保证隔离性的?如果不考虑隔离性的话又会产生什么问题呢?
这里在重点介绍一下事务的隔离级别,因为当多个线程同时开启事务操作数据库数据时,数据库要能进行隔离操作,保证每个事务的成功,以及操作数据的准确性和一致性。要保证这些操作的成功,我们的隔离性也有相应的级别,来解决不同程度的隔离问题;
首先如果不考虑隔离性,会怎样?
- **脏读:**指一个事务在处理数据的过程中,读取到另一个未提交事务的数据;
- **不可重复读:**不可重复读是指对于数据库中的某个数据,一个事务范围内的多次查询却返回了不同的结果,这是由于在查询过程中,数据被另外一个事务修改并提交了。
- **幻读:**幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
我们看到,如果不考虑隔离性,会出现以上三种导致读取数据错误的问题,那么我们每个隔离级别又分别可以解决那个问题呢?
事务隔离级别
- 读未提交(read uncommitted)
- 读已提交(read committed)
- 可重复读(repeatable read)
- 串行化(serializable)
读未提交:在这个隔离级别,所有事务都可以“看到”未提交事务的执行结果。在这种级别上,这是并发最高,一致性最差的隔离级别。读取未提交数据,也被称为“脏读”。任何问题都不能解决。
读已提交: 只能读到已经提交了的内容;一个事务开始时,只能“看见”已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。它可以解决脏读问题。
可重复读: 同一事务中可以多次读取某一数据并保证多次读取的数据相同;它可以解决不可重复读问题。并且是mysql默认的隔离级别。
串行化: 它是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突。但是它会在每个读的数据行上加锁,可能会导致大量的超时Timeout和锁竞争Lock Contention现象。它可以解决幻读。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | √ | √ | √ |
| 读已提交 | x | √ | √ |
| 可重复读 | x | x | √ |
| 串行化 | x | x | x |



