Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)

 

目录

事务的四大特性

事务的隔离级别

一、事务的并发问题

二、事务隔离级别查看与修改

三、读未提交--脏读

四、读已提交--不可重复读

五、可重复读--幻读

六、可串行化


事务的四大特性

事务特性 事务特性说明
原子性 即整个事务是一个整体,要么这个事务全部执行成功,要么全部失败
一致性
执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
隔离性 当一个事务正在对数据进行操作时,另一个事务不可以对数据进行操作,也就是多个并发事务之间相互隔离
持久性 如果执行成功,数据库崩溃,如果重新启动,数据库已经执行完的状态不会变

这4个特性是互相关联的。只有原子性才能确保一致性,才能确保隔离性,为了避免降低效率与互相感染影响,而持久性才能确保数据库非正常工作后的成功

 

事务的隔离级别

Mysql默认的隔离级别是可重复读(Repeatable Read 简称RR),可以解决脏读和不可重复读,但只可部分解决幻读!!!

隔离性(isolation):隔离性要求一个事务对数据库中数据的修改,在未提交完成前对于其他事务是不可见的

事务隔离级别 是否解决脏读 是否解决不可重复读 是否解决幻读
读未提交--Read uncommitted RU no no no
读已提交--Read committed RC yes no no
可重复读--Repeatable Read RR yes yes no
可串行化--serializable yes yes yes


一、事务的并发问题

事务并发问题 说明
脏读 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
不可重复读 事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致
幻读 系统管理员A将数据库中所有学生的成绩从具体分数改成ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,及好像发生了幻觉一样,这就叫做幻读(幻读只针对插入和删除

不可重复读和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除,解决不可重复读的问题只需要锁住满足条件的行;解决幻读需要锁表,采用最高级事务隔离级别可串行化。

 

二、事务隔离级别查看与修改

查看事务的隔离级别的命令:show variables like '%tx_isolation%';  默认是可重复读

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第1张图片

修改事务的隔离级别的命令,全局后面新建查询都生效):set global transaction isolation level read uncommitted

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第2张图片

当前窗口,当前连接会话,新建查询后失效:set session transcction isolatton level read uncommitted

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第3张图片

设置后在查看事务的隔离级别(设置了全局后,新建查询窗口查看是否生效)

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第4张图片

 

三、读未提交--脏读

1、全局修改事务的隔离级别为未提交读的命令:set global transaction isolation level read uncommitted

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第5张图片

2、新建查询开启两个窗口,在一个窗口开启事务1

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第6张图片

3、在另一个窗口开启事务2

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第7张图片

4、在事务1中,修改一张表的数据update id set age=3 where id=1;并查询age

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第8张图片

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第9张图片

5、在事务2中读取事务1修改的age数据(此时如果对数据进行操作,如果事务1进行回滚不提交此时数据将是脏数据)

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第10张图片

6、事务1进行事务回滚rollback,并查询,数据回到未修改状态

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第11张图片

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第12张图片

此时,事务2刚才的读取数据为脏数据!!!当然它也是不可重复读,同一事物出现数据不一致问题,下面继续介绍。

如果我在存钱,你在取钱两件事同时进行。我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额为1000元,就在我确认后,存钱的机器坏掉了,导致存钱失败,回滚事务,莫名其妙,你查询到了1000元就取不出来了(因为那是脏数据),此时你取不出来,又去查询,结果发现为0元(此时在一个事务读取一个数据产生了两个值),你这就纳闷了。

 

四、读已提交--不可重复读

1、全局修改事务的隔离级别为已提交读的命令:set global transaction isolation level read committed

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第13张图片

2、新建查询开启两个窗口,在一个窗口开启事务1

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第14张图片

解决了脏读:

3、在另一个窗口开启事务2

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第15张图片

 

4、此时事务1中,修改一张表的数据update id set age=3 where id=1;并查询age,未提交

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第16张图片

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第17张图片

5、事务2读取年龄,还是得到2(RC 解决了脏读

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第18张图片

不可重复读还未解决:

6、事务1提交事务commit

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第19张图片

7、事务2再次查询age,发现在这同一个事务中,第一次读取age=2,第二次读取age=3

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第20张图片

 

此时,事务2在同一个事务读取同一个数据产生了不同的值!!!

如果我在存钱,你在取钱两件事同时进行,我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额为0元,就在我确认后,存钱成功,此时你抱着好奇心去试着取一下500块钱,莫名其妙,你竟然把500元就取出来了(这就是不可重复读,在一个事务读取一个数据产生了两个值),你这就纳闷了,哇,是不是机器坏了,你继续取10000,结果失败,你再次查询,里面又出来了500数据,此时你继续纳闷...

 

五、可重复读--幻读

1、全局修改事务的隔离级别为已提交读的命令:set global transaction isolation level Repeatable Read

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第21张图片

2、新建查询开启两个窗口,在一个窗口开启事务1

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第22张图片

解决了脏读:

3、在另一个窗口开启事务2

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第23张图片

 

4、此时事务1中,修改一张表的数据update id set age=4 where id=1;并查询age,未提交

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第24张图片

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第25张图片

5、事务2读取年龄,还是得到3(可重复读事务隔离级别解决了脏读

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第26张图片

解决了不可重复读:

6、事务1提交事务commit

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第27张图片

7、事务2再次查询,age=3,跟上次读取数据值一致(RR 解决了不可重复读

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第28张图片

 

8、此时新建事务3,开启事务不开也行,即可查询最新数据

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第29张图片

 

如果我在存钱,你在取钱两件事同时进行,我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额还是为0元,就在我确认后,存钱的 机器坏掉了-导致存钱失败-回滚事务 或者 存钱成功,都对你查询余额产生不了任何影响,这会不纳闷了。

部分解决幻读:

简单的说,解决的是就是事务A在操作的时候,查询到的数据假设只有一条,其它事务对数据进行增加或者删除都影响不了事务A的查询操作。(幻读是在同一事物内查询多次结果,因为有其它事务插入影响,而造成读取结果增多不一致)

9、假设我是管理员A,新建事务4,开启事务,查询所有的数据结果只有一条

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第30张图片

10、假设管理员B进来,开启事务,并直接插入一条数据提交

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第31张图片

11、此时管理员A在再次查一下自己修改的后的信息,一查,还是一样(RR 部分解决了幻读

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第32张图片

还有可能产生问题:特地强调一下,事务A做一下更新,就能产生幻读。。

12、假设我是管理员A,新建事务4,开启事务,查询id=3的数据为null

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第33张图片

13、假设此时,管理员B开启了事务并插入id=3的数据,并提交

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第34张图片

14、管理员A,在事务4确保下id=3是否存在,再次查询

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第35张图片

15、结果还是不存在数据(部分解决幻读),管理员A就往里面插入数据id=3

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第36张图片

结果不能插进去,RR只能解决部分幻读,下面用可串化来隔离模式解决

附加知识:可重复读的实现是MVCC快照查询机制;但是还有当前读的next-key间隙锁+行锁可以解决幻读。

参考文章,解析比较清晰:快照读和当前读

 

六、可串行化

事务的最高级别,在每个读的数据行上,加上锁,使之不可能相互冲突,因此,会导致大量的超时现象

可串行化隔离级别时,在这个事务没有被提交之前,其他的线程,只能等到当前操作完成之后,才能进行操作,这样会非常耗时,而且,影响数据库的性能,通常情况下,不会使用这种隔离级别

1、设置可串行化隔离模式

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第37张图片

2、假设我是管理员A,新建事务4,开启事务,查询id=3的数据为null,读就上锁,确保数据一致性

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第38张图片

3、管理员B开启了事务并插入id=3的数据,会发现一直等待,直到出现 Lock wait timeout exceeded; try restarting transaction,管理员A正在操作对象表id,因为可串行化模式,针对同一操作对象,需要保证数据一致性,所以插入失败

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第39张图片

 

4、管理员A,在事务4确保下id=3是否存在,再次查询,别的事务对这个表的增删操作不了,确保数据一致性(解决),插入数据成功并提交

Mysql深入六:事务(特性、解决脏读、不可重复读、幻读)_第40张图片

 

 

可串化来隔离模式解决影响性能,慎用

更多可串行化读内容,可以参考博客:https://blog.csdn.net/fly2nn/article/details/61924813

可串行化这块内容太多,写的有错,欢迎评论!!

 

你可能感兴趣的:(Mysql基础,模块,事务特性,事务隔离级别,脏读,不可重复读,幻读)