一、 事务四大特性(ACID)
原子性(Atomicity):数据库把“要么全做,要么全部做”的这种规则称为原子性
隔离性(Isolation):事务之间相互隔离,不受影响,这与事务的隔离级别密切相关
一致性(Consistency):事务执行前后的状态要一致,可理解为数据一致性
持久性(Durable):事务完成之后,她对数据的修改是永恒的,即时出现故障也能够正常保持
程序访问数据库,往往是多个线程并发执行多个事务,数据库要能进行隔离操作,以保证各个线程获取数据的准确性,所以,对于不同的事务,采用不同的隔离级别会有不同的结果。
如果不考虑事务的隔离性,会发生几种问题
- 脏写(Dirty Write)
如果一个事务修改了另一个事务提交修改过的数据,就意味着发生了脏写现象。 - 脏读(Dirty Read)
如果一个事务读到了另一个未提交事务修改过的数据,就意味着发生了脏读现象。 - 不可重复读
如果一个事务修改了另一个未提交事务读取的数据,就意味着发生了不可重复读现象。 - 幻读
如果一个事务先根据某些查询条件查询出一些记录,在该事务未提交时,另一个事务写入了一些符合那些收缩条件的记录(这里指INSERT,DELETE,UPDATE 操作),就以为着发生了幻读现象
二、脏读,不可重复读,幻读
2.1 脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读
脏读最大的问题就是可能会读到不存在的数据。比如在上图中,事务B的更新数据被事务A读取,但是事务B回滚了,更新数据全部还原,也就是说事务A刚刚读到的数据并没有存在于数据库中。 从宏观来看,就是事务A读出了一条不存在的数据,这个问题是很严重的
2.2 不可重复读
不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
2.3 幻读
幻读错误的理解
幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得
到 15 条记录。 这其实并不是幻读,既然第一次和第二次读取的不一致,那不还是不可重复读吗,所以这是
不可重复读的一种
正确的理解
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数
据状态无法支撑后续的业务操作。
更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在
无法插入,此时就发生了幻读
查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。
这才是所有的幻读。 不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影
三、事务的隔离级别
上面我们介绍了事务执行过程中可能会遇到的一些现象,这些现象会对事务的一致性产生不同程度的影响。上面严重性现象排序
脏写 > 脏读 > 不可重复读 > 幻读
为了解决上面的问题,数据库就指定了一个隔离级别标准,隔离级别越低,就越可能发生严重的问题。
READ UNCOMMITTED:未提交读(读未提交)
READ COMMITTED:已提交读(读已提交)
REPEATABLE READ:可重复读
SERIALIZABLE:可串行化
3.1 Read uncommitted(读未提交)-ru
事务B读取到了事务A未提交的数据
A事务在写数据时,不允许B事务进行写操作,但允许B事务进行读操作
于是 B就会读到A事务写入,但没提交的数据,于是出现脏读
解决了更新丢失,但会出现脏读
3.2 Read committed(读已提交)-rc
写事务提交之前不允许其他事务的读操作,可以解决脏读问题。
但会出现一个事务范围内两个相同的查询却返回了不同数据
解决了更新丢失和脏读问题
3.3 Repeatable read(可重复读取)-rr
在开始读取数据(事务开启)时,不再允许修改操作,这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,但是有时可能会出现幻读
解决了更新丢失、脏读、不可重复读、但是还会出现幻读
3.4 Serializable(串行化)
要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。
序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行
可以避免脏读、不可重复读,幻读
四、总结
针对不同的隔离级别,并发事务执行过程中可以发生不同的现象
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 可能 | 可能 | 可能 |
READ COMMITTED | 不可能 | 可能 | 可能 |
REPEATABLE READ | 不可能 | 不可能 | 可能 |
SERIALIZABLE | 不可能 | 不可能 | 不可能 |
也就是说:
在 READ UNCOMMITTTED 隔离级别下,可能发生脏读,不可重复读和幻读现象;
在 READ COMMITTED 隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;
在 REPEATABLE READ 隔离级别下,可能发生幻读现象,但是不可能发生脏读和不可重复读现象;
在 SERIALIZABLE 隔离级别下,上述各种现象都不可能发生。
脏写: 脏写这个现象对一致性影响太严重了,无论哪种隔离级别,都不允许出现脏写的情况发生
Oracle仅支持两种隔离级别:Read Committed与Serializable,默认基本为RC
Mysql的InnoDB 引擎才支持事务,默认事务隔离级别为:REPEATABLE READ(可重复读)-RR
在RR这一隔离级别下,只能解决部分幻读问题,不能解决全部的幻读问题
五、案例演示
首先设置数据库的隔离级别为读未提交
set session transaction isolation level read UNCOMMITTED
据库的隔离级别设置为读已提交
set session transaction isolation level read committed
隔离级别设置为可重读
set session transaction isolation level REPEATABLE READ
用命令 SELECT @@tx_isolation;查询下设置的级别
假设我们现在有一张表,表中有 主键 ID 和 年龄 age 两个字段
执行一条 SQL:insert into t value (1, 1)
接下来我们开启两个事务,从上到下时间线
执行时间顺序 | 事务A | 事务B |
---|---|---|
1 | 启动事务,查询得到值 1 | 启动事务 |
2 | - | 查询得到值 1 |
3 | - | 将 1 改成 2 |
4 | 查询得到值 V1 | - |
5 | - | 提交事务 B |
6 | 查询得到值 V2 | - |
7 | 提交事务A | - |
8 | 查询得到值 V3 | - |
- 读未提交隔离级别下
两个事务同时开启
事务B 获取值1
事务B 将其1改成2
事务A 得到 V1 的值为 2,原因:此隔离级别下,事务B 虽然还没有提交,但是 事务A 是可以看到被修改的结果的
事务B 提交
事务A 拿到 V2 的值为 2
事务A 提交
事务A 拿到 V3 的值为 2
- 读已提交隔离级别下
两个事务同时开启
事务B 获取值1
事务B 将其1该为2
事务A 得到 V1 的值为 1,原因:读已提交隔离级别下,事务B 的更新再提交后才能被 A 看到,此刻还没有提交
事务B 提交
事务A 查询 V2 的值为 2,原因:事务B已经提交过了,则事务A 可以看到事务B的改动
事务A 提交
事务A 查询 V3 的值为 2
- 可重复读
用户在执行当前事务期间看到的数据前后必须是一致的。即从事务开始之前,没有外部事务进行写操作,则到该事务提交这段时间访问到的数据是一致的,只能看到自己所修改的数据
两个事务同时开启
事务B 获取值1
事务B 将其1该为2
事务A 得到的值为 1,原因:可重复读隔离级别下,参考上面文字
事务B 提交
事务A 查询 V2 得到的值为 1,原因:可重复读隔离级别下,参考上面文字
事务A 提交
事务A 查询 V3 得到的值为 2,原因:当前事务已经提交,可以访问比当前事务提交更早一些提交事务对数据库进行的写操作
转载:https://blog.csdn.net/m0_58987515/article/details/125805581