并发事务下,不同隔离级别可能出现的问题
- 1、事务的 ACID
- 2、并发事务下,不同隔离级别可能出现的问题
- 2.1、脏写
- 2.2、脏读
- 2.3、不可重复读
- 2.4、幻读
- 3、SQL 中的四种隔离级别
1、事务的 ACID
-
原子性(Atomicity)
:原子性意味着事务是一个不可分割的最小工作单元,事务内的操作要么全部完成,要么全部不完成,不可能停滞在中间某个环节。也就是说,事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
-
一致性(Consistency)
:指事务必须使数据库从一个合法性状态,变换到另一个合法性状态。这里的状态是指数据满足完整性约束(如主键约束、外键约束、检查约束等)的状态,这里说的状态是语义上的,和具体业务有关。一致性就是保证事务执行后,数据库中的数据仍然是有意义的。
-
隔离性(Isolation)
:指一个事务的执行不能被其他事务干扰
,即一个事务内部的操作及使用的数据对并发
的其他事务是隔离
的,并发执行的各个事务之间不能互相干扰。
-
持久性(Durability)
:一旦事务提交,则其结果永久保存在数据库中。即使系统发生故障,数据库也能将数据恢复到事务成功执行的状态。持久性是通过事务日志
保证的。
数据库事务的状态通常包括以下几种:
- 活动状态(Active):事务正在执行,尚未提交或回滚。
- 部分提交状态(Partially Committed):事务中的一部分操作已经完成并提交,但其他操作还在执行中。
- 失败状态(Failed):事务执行失败,需要进行回滚操作。
- 成功状态(Succeeded):事务执行成功,所有的操作都已经提交。
- 中止状态(Aborted):事务在执行过程中被中断,比如由于系统故障或者数据库故障。
2、并发事务下,不同隔离级别可能出现的问题
并发执行事务过程中,不同隔离级别下可能出现的问题
重要程度排序: 脏写
> 脏读
> 不可重复读
> 幻读
2.1、脏写
对于两个事务A、B,如果事务A 修改了 另一个未提交事务
B 修改过的数据,那就是发生了脏写
。以下示例,事务A已经提交成功,但是事务B回滚了,最终 no=1 的数据没有任何变化。
发生时间编号 | 事务 A | 事务 B |
---|---|---|
① | begin; | |
② | begin; | |
③ | update tb_table set name = ‘对对对’ where no = 1; | |
④ | update tb_table set name = ‘错错错’ where no = 1; | |
⑤ | commit; | |
⑥ | rollback; |
2.2、脏读
举例:对于两个事务A、B,事务A读取了已经被事务B 更新
但未被提交
的数据,之后事务B 回滚
了,事务A读取的内容就是临时且无效
的。
发生时间编号 | 事务 A | 事务 B |
---|---|---|
① | begin; | |
② | begin; | |
③ | update tb_table set name = ‘对对对’ where no = 1; | |
④ | select name from tb_table where no = 1; (发生脏读,这里读到name = ‘对对对’) | |
⑤ | commit; | |
⑥ | rollback; |
2.3、不可重复读
举例:对于两个事务A、B,事务A读取了一个字段,然后事务B 更新了该字段,之后事务A再次读取同一个字段,值就不同了,这种情况就是不可重复读。
发生时间编号 | 事务 A | 事务 B |
---|---|---|
① | begin; | |
② | select name from tb_table where no = 1; (这里读到name = ‘原始数据’) | |
③ | begin; | |
④ | update tb_table set name = ‘对对对’ where no = 1; | |
⑤ | commit | |
⑥ | select name from tb_table where no = 1; (发生不可重复读,这里读到name = ‘对对对’) | |
⑦ | commit; |
2.4、幻读
对于两个事务A、B,事务A
从一个表中读取
了一个字段,然后事务B
在该表中插入
了一些新的行。之后事务A 再次读取
同一个表,就会多出几行数据
,这就是幻读
。幻读侧重的方面是某一次的select 操作得到的结果,所表征的数据状态无法支撑后续的业务操作。对于MySQL而言,幻读是事务在插入事先检测不存在的记录时,发现这条数据已经存在了,无法支撑后续的插入操作。
发生时间编号 | 事务 A | 事务 B |
---|---|---|
① | begin; | |
② | select * from tb_table where no > 1; (这里读到了2条数据) | |
③ | begin; | |
④ | insert into tb_table values(4,‘测试’); | |
⑤ | commit | |
⑥ | select * from tb_table where no > 1; (发生幻读,这里读到了3条数据) | |
⑦ | commit; |
说明:
- 以上第④步,如果删掉了id=2 的数据,导致第⑥步只读到1条数据,这种情况不算幻读。幻读强调的是一个事务按照某个
相同条件多次读取
记录时,后读取时读到了之前没有读到的记录
。 - 对于先前已经读到的记录,之后又读取不到,这相当于对每条记录都发生了
不可重复读
的现象。
3、SQL 中的四种隔离级别
隔离级别越低,并发问题发生的就越多。 SQL 标准
中设立的 4 个隔离级别:
-
READ UNCOMMITTED
:读未提交
,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读
。 -
READ COMMITTED
:读已提交
,一个事务只能看到已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读
,但是**不可避免 不可重复读、幻读
**。 -
REPEATABLE READ
:可重复读(MySQL默认隔离级别)
,事务A 在读到一条数据后,此时事务B对该数据进行修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读
,但是**不可避免幻读
**。重点:MySQL 在可重复读级别下,是可以禁止幻读的。
-
SERIALIZABLE
:可串行化
,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。可以避免所有的并发问题,但是**性能十分低下
。能避免脏读、不可重复读、幻读
**。
不同的数据库厂商,对SQL标准中规定的四种隔离级别支持不一样。以下是 SQL 标准中规定(注意:这里不是 MySQL)
,对不同隔离级别下,并发事务可以发生不同严重程度的问题:
隔离级别 | 脏读 | 不可重复度 | 幻读 | 加锁读 |
---|---|---|---|---|
READ UNCOMMITTED | 发生 | 发生 | 发生 | 避免 |
READ COMMITTED | 避免 | 发生 | 发生 | 避免 |
REPEATABLE READ | 避免 | 避免 | 发生 | 避免 |
SERIALIZABLE | 避免 | 避免 | 避免 | 发生 |
.