mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?

可重复读解决了更新带来的不可重复读问题,但是没有解决插入或者删除带来的幻读问题。这句话,是老八股文了。
但真实情况是这样的吗?这个验证不麻烦,我们可以动手来验证一下。
我使用的是免安装版本的windows mysql8.0.31,用power shell登录mysql客户端,命令行操作mysql。
搞一个测试表user1,里面有一条初始化数据,如下:
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第1张图片
看一下mysql默认的隔离级别
select @@transaction_isolation;
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第2张图片
可以看到mysql的默认隔离级别就是可重复读,我们不用修改。
下面开始操作。
1、事务A,执行begin,然后执行update语句,将name由"张三"改成"李四",此时先别提交。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第3张图片
2、事务B,执行begin,然后执行select语句,此时也别提交。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第4张图片
事务B为什么要执行一个select语句?
其实是,如果我们只是执行一个begin命令,此时mysql不会产生事务。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第5张图片
需要执行一个命令才会有事务,不一定是select,其他语句也可以。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第6张图片
然后事务A的update语句提交,如下图。可以看到,事务A提交后,再次查询id=1的数据,name已经由"张三"变成了"李四"
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第7张图片
我们再看一下事务B的窗口,执行一下select。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第8张图片
可以看到,id=1的name字段还是"张三",并没有发生变化。
至此,我们就验证了:“可重复读” 隔离级别下,确实可以解决更新语句带来的 “不可重复读” 问题。
那是否解决了幻读呢?
我们在事务A插入一条语句,提交。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第9张图片
在事务A可以看到,新增了一条数据,id=2,name=“王五”
按照八股文的说法,我们此时在事务B执行select,应该是可以看到新增的这条id=2的数据的。
我们验证一下,在事务B上执行查询。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第10张图片
并没有看到新增的数据!
嗯?难道mysql的"可重复读"隔离级别解决了幻读问题吗?
如果是这样,那Serializable隔离级别就没啥意义了吧?
实际上,mysql的"可重复读"隔离级别解决了select的幻读问题,没有解决update、insert、delete的幻读问题,怎么理解呢?
我们举个例子。
事务A再执行一个语句,插入一个id=3,name="李四"的数据,
insert into user1 (id,name) values (3,“李四”);
执行完,提交。可以看到新增成功。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第11张图片
回到事务B,执行一个更新语句,将表中name字段为"李四"的数据都改成aaaaaa
update user1 set name = “aaaaaa” where name = “李四”;
如果可重复读能解决更新语句带来的幻读问题,我们看到的结果应该是:

id name
1 aaaaaa
2 王五

但实际上,是这样的

id name
1 aaaaaa
2 王五
3 aaaaaa

这说明,事务B读到了事务提交的insert语句,然后将其name字段改为了aaaaaa,所以"可重复读"隔离级别并没有解决insert的幻读问题,同理update、delete也是一样的。
那是什么原因导致的以上情况呢?
这里面涉及到两个概念,快照读、当前读。
先说快照读。
在事务A开启事务,未提交事务时,事务B开启了事务,执行了查询操作,此时事务B读取的就是数据库的一个快照。事务A此时操作数据提交,影响不到事务B的读操作,因为MVCC的缘故。MVCC其实就是借助版本号,结合undo日志实现,有时间再写一下这个,此处按下不表。
但是事务B如果执行update、delete、insert,这就不是快照读了,而是当前读,读取的是表最新的数据。类似于select …for update操作。
如果事务B执行select操作,并且想读到最新的数据。有2个方法。
一个是加select+for update,执行当前读。
另一个就是等待事务A提交后,再开启读事务。
除此之外,我们还可以上更高的隔离级别:serializable级别,也就是串行化。这个级别是怎么解决以上问题的呢?
事务A开启事务执行insert语句。
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第12张图片
事务B开启事务执行select语句,如下图:
mysql “可重复读“ 解决了哪些问题,没有解决哪些问题?_第13张图片
可以看到,select直接卡住,然后等待锁超时了,也就是说在serializable级别下,写请求没有提交时,不允许开启其他事务。
参考:
1、https://dev.to/techschoolguru/understand-isolation-levels-read-phenomena-in-mysql-postgres-c2e
2、https://blog.csdn.net/qq_43295093/article/details/120561978

你可能感兴趣的:(mysql,数据库,java)