所谓幻读是事务的隔离性里面的概念,Mysql中的Innodb引擎是支持事务的,所以本章讲解Innodb的事务特性之一隔离性中的幻读是如何被解决的。
前提知识:
事务的四大特性:ACID
A:Atomic(原子性):一系列操作构成,要么都执行,要么都不执行
C:Consistency(一致性):数据更改前即更改后都是符合业务逻辑的,不会出现脏数据。
I:Isolation(隔离性):读事务和写事务在时间轴上有交叉时,写事务导致的数据的修改对读事务的可见性上的一种隔离。
D:Durability(持久性):数据更改之后会持久化。
隔离性等级:
隔离性分为四个等级:未提交读、已提交读、可重复读、可序列化。随着隔离等级的提高,读取的数据一致性越高,但并发性越低。
脏读 不可重复读 幻读
未提交读 未解决 未解决 未解决
已提交读 解决 未解决 未解决
可重复读 解决 解决 未解决(可重复读为mysql默认)
可序列化 解决 解决 解决
没有隔离性产生的问题:
脏读:A读取到了B修改(update)未提交的数据,然后B回滚,那么A读到的数据是脏数据。
不可重复读:事务A多次读取同一条数据,事务B在事务A读取的过程中,对数据做了更新并提交,导致A多次读到的数据不一致。
幻读:系统管理员将班上所有的成绩都改成了ABCDE五个等级,但是系统管理员B就在这个时候插入(insert or delete)一条具体分数的数据,这是系统管理员发现有一条数据没有改过了,就好像发生幻读一样。
总结:不可重复读和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增和删除。解决不可重复读只需要锁行,解决幻读需要锁表。
测试演示:
未提交读(read uncommitted):设置命令:set session transaction isolatin level read uncommitted;设置完成后,当session1修改或者新增数据,sessionA,sessionB都没有commit;在session2中会读到session未提交的数据。(sessionA、B都可不commit)
提交读(read committed)RC:设置命令:set session transaction isolatin level read committed;设置完成后,当session1修改或者新增数据,sessionA commit sessionB没有commit;在session2中会读到sessionA修改的数据。(sessionA必须commit,sessionB可不commit)
可重复读(repeatable read)RR:设置命令:set session transaction isolatin level repeatable read;设置完成后,当session1修改或者新增数据,sessionA commit sessionB也必须commit;在session2中才会会读到sessionA修改的数据。(sessionA必须commit,sessionB也必须commit)
RR mvcc解决幻读问题
Mysql作为数据库,保存了一份数据,每时每刻都会接收很多请求进来,在如何保证数据安全的基础上提高并发性,就是一直在追求的目标。在时间轴上,如果是读请求与读请求有交叉,那么是没有问题的,不会出现数据同时被修改的可能,并发理论上无上限;写请求与写请求有交叉不在本笔记讨论范围内;如果是读请求与写请求在时间轴上有交叉,虽然不会出现数据不安全的可能,但是对于读请求失去了一个重要的特点:不能保证一个事务中读请求前后的一致性,也就是说不能保证在一个事务中第一次读和第二次读到的是相同的数据。因为这个原因,所以才有了所谓的隔离性!
从上面的表格中,虽然可序列化解决了所有问题,可序列化其实就相当于synchronized,把所有的请求进行同步,大大降低了Mysql的并发性,实际上没人会采用。我们的目标就是保证数据安全、数据读取一致的前提下,尽量提高并发性。Mysql默认的数据隔离级别是可重复读,但可重复读本身并没有解决幻读。
Innodb解决幻读的方式:MVCC(多版本并发控制)
其实这里就是讲一个概念模型,我相信这个概念模型可以让你看清MVCC:
Innodb的表实际上会有两个隐藏列:新增版本号、删除版本号。新增版本号记录的是这条数据行新增时记录的系统版本号,删除版本号记录的是这条数据被删除时记录的系统版本号。注意:每次获取系统版本号,都是获取一个全局唯一且递增的一个新的系统版本号。这就和HBase有点类似,在原来的数据的定位维度上加了一个:时间维度。
Innodb进行一些操作时,会颁发系统版本号:在事务开始时,事务获取一个系统版本号,以方便在事务内部使用;在数据行被新增或删除时,获取版本号记录在版本号列中。
写事务:事务结束时,根据写的类型,如果是INSERT,那么将获取一个系统版本号放在对应数据行的新增版本号列中;如果是DELETE,那么将获取一个系统版本号放在对应的数据行的删除版本号列中;如果是UPDATE,那么会获取一个系统版本号记录在对应的旧数据行的删除版本号中,同时获取一个系统版本号记录在添加的新的数据行的新增版本号列中。(是的,你没有看错,UPDATE的时候,同一主键暂时存在两条数据!)
读事务:事务开始时获取一个系统版本号,读取数据时,如果行的新增版本号 < 事务版本号 < 行的删除版本号,那么才可以读取。
每个表有两个隐藏的列,一个是新增版本号,一个是删除版本号,每次进行新增和删除时,会拿一个系统版本号,放在对应的隐藏列上,如果是修改(修改其实就是先删除后新增)会同时存放两个隐藏列
MVCC只在提交读、可重复读两个隔离级别上工作。可以对照上图时间轴,思考一下,不可重复读、幻读的解决过程。
总结:Mysql的Innodb存储引擎就是通过MVCC的方式,解决了不可重复读、幻读的问题,保证了隔离性,可以在不加锁(保证并发性)的情况下,保证读取数据的一致性。
查询mysql事务隔离级别
1.查看当前会话隔离级别
select @@tx_isolation;
2.查看系统当前隔离级别
select @@global.tx_isolation;
3.设置当前会话隔离级别
set session transaction isolatin level repeatable read;
4.设置系统当前隔离级别
set global transaction isolation level repeatable read;
5.命令行,开始事务时
set autocommit=off 或者 start transaction
关于隔离级别的理解
1.read uncommitted
可以看到未提交的数据(脏读),举个例子:别人说的话你都相信了,但是可能他只是说说,并不实际做。
2.read committed
读取提交的数据。但是,可能多次读取的数据结果不一致(不可重复读,幻读)。用读写的观点就是:读取的行数据,可以写。
3.repeatable read(MySQL默认隔离级别)
可以重复读取,但有幻读。读写观点:读取的数据行不可写,但是可以往表中新增数据。在MySQL中,其他事务新增的数据,看不到,不会产生幻读。采用多版本并发控制(MVCC)机制解决幻读问题。
4.serializable
出自于:https://blog.csdn.net/weixin_35794878/article/details/109705693