一、事务基本定义(ACID)
1、原子性(Atomicity)
:事务一旦开始,那么我们只能有两个结果,要么全部执行完成,要么全部都不做,不能停滞在事务中途。如果在执行中出错,必须把事务回滚到事务开始前。
2、一致性(Consistency)
:事务开始前和结束后,数据库的完整性约束没有被破坏 。比如买东西,付款的时候,我扣钱成功了,对方也一定要收到钱,不能出现我扣了钱,对方却没收到。
3、隔离性(Isolation)
:同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability)
:事务一旦完成,那必须保存到数据库,并且不能回滚。
二、事务的并发问题
首先我们搞懂这几个由于并发访问导致的数据读取问题
1、脏读(读取到了执行中未提交的数据)
事务A读取到了事务B尚未提交的数据,如果事务B回滚了操作,那么事务A就是读到了脏数据。
这种情况主要发生在查询数库时的并发问题
例如:
这个场景就读取到了未提交的脏数据了,产生了脏读
时间轴 | 查询事务 | 新增事务 |
---|---|---|
1 | 开始查询 | 开始修改 |
2 | 查询到数据为100 | 修改数据为100 |
3 | 返回查询数据100 | 操作发生未知错误事务回滚 |
4 | 提交事务 | 数据回滚成修改前数据 |
2、不可重复读(事务执行中多次读取,数据内容不一致)
事务A在读取数据时,由于整个事务A操作比较多,事务需要多次读取同一条数据时,间隔需要比较长的时间 。事务A在第一次读取数据,比如读取到年龄为15岁,事务B执行更改操作,将年龄更改为25岁,此时事务A第二次读取到年龄时,发现其年龄是25岁,和之前的数据不一样了,也就是数据不重复了,从而导致了不可重复读。
例如:
这个场景事务A前后两次读取到的数据不一致导致不可重复读
时间轴 | 事务A | 修改事务 |
---|---|---|
1 | 开始事务 | |
2 | 第一次查询到A年龄为15 | |
3 | 开始事务 | |
4 | 其它操作 | |
5 | 更改A年龄为25 | |
6 | 提交事务 | |
7 | 第二次查询到A年龄为25 |
3、幻读(事务执行中多次读取,数据总量不一致)
事务A在读取数据时,由于整个事务A操作比较多,事务需要多次统计数据的总量,间隔需要比较长的时间 。第一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A第二次读取的数据总量和第一次统计的不一样,多了几条数据,成为幻读。
例如:
这个场景事务A前后两次读取到的数据总量不一致导致幻读
时间轴 | 事务A | 批量新增事务 |
---|---|---|
1 | 开始事务 | |
2 | 第一次查询,数据总量为100条 | |
3 | 开始事务 | |
4 | 其它操作 | |
5 | 新增100条数据 | |
6 | 提交事务 | |
7 | 第二次查询,数据总量为200条 |
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。
三、MySQL事务隔离级别
上面我们说了会存在事务的并发问题,那么接下来看一看,MySQL给我们提供的事务隔离级别分别解决了什么问题
MySQL定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
1、读未提交(read-uncommitted)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读。
2、不可重复读(read-committed)
事务中只能看到已经提交事务的数据,这种隔离级别不支持不可重复读,如果有其它事务正在处理新增并且提交了,查询前后数据不一致导致不可重复读
3、可重复读(repeatable-read)
MySQL的默认的事务隔离级别(简称:RR),它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过还是没有解决幻读的问题。后面我们会分享关于
InnoDB存储引擎通过多版本并发控制(Multiversion Concurrency Control 简称MVCC)
机制解决了该问题。
4、串行化(serializable)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,解决所有并发问题。由于牺牲了并发能力,而且非常占用资源,实际开发中不会选择该级别的事务隔离。
不同事务隔离级别分别解决的并发问题参照表:
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |