事务隔离级及脏读、幻读和不可重复读
- 事务
定义:是数据库操作的最小工作单元,独立不可分割,要么都执行、要么都不执行。
- 事务的四个特性
1 、原子性 :强调事务的不可分割。
2 、一致性 :事务开始之前和结束以后,数据库的完整性约束没有被破坏。
3 、隔离性 :并发执行的各个事务之间不能互相干扰。
4 、持久性 :事务一旦提交,数据就持久化到数据库。
- 隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。
- Read uncommitted(读未提交):就是一个事务可以读取另一个未提交事务的数据。如下,A、B两个事务在并发的情况下访问同一数据时,B事务读取了A事务修改但未提交的数据。
A事务 | B事务 | |
1 | A事务开启 | |
2 | B事务开启 | |
3 | A事务读取数据为1000,并且使用了update,把数据改为2000 | |
4 | B事务读取数据为2000 | |
5 | A事务提交 |
- Read committed(读提交):就是一个事务要等另一个事务提交后才能读取数据。这种事务隔离级别依旧会出现问题,很多人会有疑问,B事务读取的内容不是恰好为A提交后的事务吗,A和B事务都没有错,这样能有什么问题!?不要着急慢慢看,咱们先把4种隔离级别分析完。
A事务 | B事务 | |
1 | A事务开启 | |
2 | B事务开启 | |
3 | A事务读取数据为1000,并且使用了update,把数据改为2000 | |
4 | A事务提交 | |
5 | B事务读取的数据为2000 |
- Repeatable read(重复读):就是在开始读取数据(事务开启)时,不再允许修改操作,不过这样表述不太严谨。准确的说是B事务对数据的修改不会影响到A事务读取的数据,呃呃呃,什么鬼????我自己都搞糊涂了。于是我上网查了一下详细的资料,就是为了搞清楚原理。
网上这样描述:InnoDB在每行记录后面保存两个隐藏的列来,分别保存了这个行的创建时间和行的删除时间。这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1。在读取事务开始时,系统会给当前事务一个 版本号,事务会读取版本号<=当前版本号的数据。此时如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然而然就读不到更新后的数据了。
这里把版本号抽象为列属性version,便于理解。
id | data | version |
1 | 1000 | 01 |
表1
id | data | version |
1 | 2000 | 02 |
表2
A事务 | B事务 | |
1 | A事务开启,读取id为1且版本号为01的数据 | |
2 | B事务开启,读取id为1且版本号为01的数据 | |
3 | A事务update了data的数据,data等于2000,这条数据的版本号变为2 | |
4 | A事务提交 | |
5 | B事务所获取的该数据的版本号仍为1,B事务再次读取数据,结果仍为1000 |
- Serializable (序列化):Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。这里不做过多介绍。
- 脏读(读未提交可能引起)
A和B两个事务去读取一条数据,A事务对该条数据进行了修改,B事务恰好读取到了A修改后的数据,但是A事务并没有提交,而是在接下来的过程中发生了回滚或者再次修改了这条数据,但是B事务并不知道,那么可以说B事务读
取的数据为脏数据,B事务进行了脏读。
A事务 | B事务 | |
1 | A事务开启 | |
2 | B事务开启 | |
3 | A事务读取数据为1000,并且update数据为2000 | |
4 | B事务读取数据为2000 | |
5 | A事务发生了回滚,数据重新变成1000 | |
6 | B事务执行update操作,在2000的基础上加1000 | |
7 | 提交事务,结果为3000(脏数据)。实际数据应为2000 |
- 不可重复读(读未提交、读提交)
A和B两个事务去读一条数据,B读取数据后A事务对该条数据进行了修改并提交,当B事务再次读取这条数据时,发现数据变了。这里就回答了上面提到的问题,把数据隔离级别设置为读提交就有可能在并发时出现不可重读的错误。
A事务 | B事务 | |
1 | B事务开启 | |
2 | A事务开启 | |
3 | B事务读取数据为1000 | |
4 | A事务执行了update,数据变为2000 | |
5 | A事务进行了提交 | |
B事务再次读取数据,数据为2000 | ||
注 | B事务对同一条数据前后读取了两次,然而结果却不同,这就是不可重复读 |
- 幻读(数据的插入或删除引起)
A事务 | B事务 | |
1 | A事务开启 | |
2 | 读取的数据条数为100 | |
3 | B事务开启 | |
4 | 新增能加了50条数据 | |
5 | 提交事务 | |
6 | A事务再次读取数据条数,发现为150条 | |
注 | A前后两次读取的数据条数不相同,此为幻读 |
- 不可重复读和幻读的区别
不可重复度是针对update操作,幻读是针对insert操作delete操作
- 事务隔离级别与脏读、幻读和不可重读的关系(√:会出现 ×:不会出现)
脏读 | 不可重复读 | 幻读 | |
读未提交 | √ | √ | √ |
读提交 | × | √ | √ |
可重复度 | × | × | √ |
序列化 | × | × | × |