目录
事务的四大特性
事务的隔离级别
一、事务的并发问题
二、事务隔离级别查看与修改
三、读未提交--脏读
四、读已提交--不可重复读
五、可重复读--幻读
六、可串行化
事务特性 | 事务特性说明 |
原子性 | 即整个事务是一个整体,要么这个事务全部执行成功,要么全部失败 |
一致性 |
执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
|
隔离性 | 当一个事务正在对数据进行操作时,另一个事务不可以对数据进行操作,也就是多个并发事务之间相互隔离 |
持久性 | 如果执行成功,数据库崩溃,如果重新启动,数据库已经执行完的状态不会变 |
这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%'; 默认是可重复读
修改事务的隔离级别的命令,全局(后面新建查询都生效):set global transaction isolation level read uncommitted
当前窗口,当前连接会话,新建查询后失效:set session transcction isolatton level read uncommitted
设置后在查看事务的隔离级别(设置了全局后,新建查询窗口查看是否生效)
1、全局修改事务的隔离级别为未提交读的命令:set global transaction isolation level read uncommitted
2、新建查询开启两个窗口,在一个窗口开启事务1
3、在另一个窗口开启事务2
4、在事务1中,修改一张表的数据update id set age=3 where id=1;并查询age
5、在事务2中读取事务1修改的age数据(此时如果对数据进行操作,如果事务1进行回滚不提交此时数据将是脏数据)
6、事务1进行事务回滚rollback,并查询,数据回到未修改状态
此时,事务2刚才的读取数据为脏数据!!!当然它也是不可重复读,同一事物出现数据不一致问题,下面继续介绍。
如果我在存钱,你在取钱两件事同时进行。我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额为1000元,就在我确认后,存钱的机器坏掉了,导致存钱失败,回滚事务,莫名其妙,你查询到了1000元就取不出来了(因为那是脏数据),此时你取不出来,又去查询,结果发现为0元(此时在一个事务读取一个数据产生了两个值),你这就纳闷了。
1、全局修改事务的隔离级别为已提交读的命令:set global transaction isolation level read committed
2、新建查询开启两个窗口,在一个窗口开启事务1
解决了脏读:
3、在另一个窗口开启事务2
4、此时事务1中,修改一张表的数据update id set age=3 where id=1;并查询age,未提交
5、事务2读取年龄,还是得到2(RC 解决了脏读)
不可重复读还未解决:
6、事务1提交事务commit
7、事务2再次查询age,发现在这同一个事务中,第一次读取age=2,第二次读取age=3
此时,事务2在同一个事务读取同一个数据产生了不同的值!!!
如果我在存钱,你在取钱两件事同时进行,我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额为0元,就在我确认后,存钱成功,此时你抱着好奇心去试着取一下500块钱,莫名其妙,你竟然把500元就取出来了(这就是不可重复读,在一个事务读取一个数据产生了两个值),你这就纳闷了,哇,是不是机器坏了,你继续取10000,结果失败,你再次查询,里面又出来了500数据,此时你继续纳闷...
1、全局修改事务的隔离级别为已提交读的命令:set global transaction isolation level Repeatable Read
2、新建查询开启两个窗口,在一个窗口开启事务1
解决了脏读:
3、在另一个窗口开启事务2
4、此时事务1中,修改一张表的数据update id set age=4 where id=1;并查询age,未提交
5、事务2读取年龄,还是得到3(可重复读事务隔离级别解决了脏读)
解决了不可重复读:
6、事务1提交事务commit
7、事务2再次查询,age=3,跟上次读取数据值一致(RR 解决了不可重复读)
8、此时新建事务3,开启事务不开也行,即可查询最新数据
如果我在存钱,你在取钱两件事同时进行,我刚确定存了1000块(本来银行卡为0元),此时刚好你进来查询,此时余额还是为0元,就在我确认后,存钱的 机器坏掉了-导致存钱失败-回滚事务 或者 存钱成功,都对你查询余额产生不了任何影响,这会不纳闷了。
部分解决幻读:
简单的说,解决的是就是事务A在操作的时候,查询到的数据假设只有一条,其它事务对数据进行增加或者删除都影响不了事务A的查询操作。(幻读是在同一事物内查询多次结果,因为有其它事务插入影响,而造成读取结果增多不一致)
9、假设我是管理员A,新建事务4,开启事务,查询所有的数据结果只有一条
10、假设管理员B进来,开启事务,并直接插入一条数据提交
11、此时管理员A在再次查一下自己修改的后的信息,一查,还是一样(RR 部分解决了幻读)
还有可能产生问题:(特地强调一下,事务A做一下更新,就能产生幻读。。)
12、假设我是管理员A,新建事务4,开启事务,查询id=3的数据为null
13、假设此时,管理员B开启了事务并插入id=3的数据,并提交
14、管理员A,在事务4确保下id=3是否存在,再次查询
15、结果还是不存在数据(部分解决幻读),管理员A就往里面插入数据id=3
结果不能插进去,RR只能解决部分幻读,下面用可串化来隔离模式解决
附加知识:可重复读的实现是MVCC快照查询机制;但是还有当前读的next-key间隙锁+行锁可以解决幻读。
参考文章,解析比较清晰:快照读和当前读
事务的最高级别,在每个读的数据行上,加上锁,使之不可能相互冲突,因此,会导致大量的超时现象
可串行化隔离级别时,在这个事务没有被提交之前,其他的线程,只能等到当前操作完成之后,才能进行操作,这样会非常耗时,而且,影响数据库的性能,通常情况下,不会使用这种隔离级别
1、设置可串行化隔离模式
2、假设我是管理员A,新建事务4,开启事务,查询id=3的数据为null,读就上锁,确保数据一致性
3、管理员B开启了事务并插入id=3的数据,会发现一直等待,直到出现 Lock wait timeout exceeded; try restarting transaction,管理员A正在操作对象表id,因为可串行化模式,针对同一操作对象,需要保证数据一致性,所以插入失败
4、管理员A,在事务4确保下id=3是否存在,再次查询,别的事务对这个表的增删操作不了,确保数据一致性(解决),插入数据成功并提交
可串化来隔离模式解决影响性能,慎用
更多可串行化读内容,可以参考博客:https://blog.csdn.net/fly2nn/article/details/61924813
可串行化这块内容太多,写的有错,欢迎评论!!