• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Repeatable Read幻读问题解决

武飞扬头像
DannyIdea
帮助1

相信有不少的同学都有使用过MySQL内置的事务属性,我觉得相对比于其他的关系性数据库来说,MySQL的事务特性确实是一个非常吸引人去使用的点。那么本文将会来带大家深入探讨一个问题,就是为什么MySQL到了5.7之后,Repeatable Read隔离级别是如何解决幻读问题的。

不同的隔离级别分别有什么作用

Read UnCommited

读取到别的事务还没有提交的内容,不能很好的保证事务的隔离性。会有脏读,不可重复读,幻读的问题。

Read Commited

只能读取到最新已经提交的事务,能够保证读取数据的最新性,但是可能会存在幻读,不可重复读的问题。

Repeatable Read

同一个事务里面,每次的读取都是相同的,但是依然可能有幻读的问题。

Serializable

序列化级别,我觉得是可以完全避免掉幻读,不可重复读,脏读三种问题的级别了,但是一旦开启,相当于就是每个事务都变成了单线程去执行,其性能是会大大降低的。

幻读,不可重复读,脏读的区别

其实这三者我觉得就是一个概念性的东西,大家大概知道下就可以了。 不可重复读:事务的前后两次读取结果都不相同,其实所谓的幻读和脏读就是从这个大类型下延伸出来的。 脏读:读取到了未提交的事务数据。 幻读:同一条sql读取到了和之前不同的结果,例如前一刻count出来是8条记录,后一刻count出来是9条记录。幻读更多强调的是数据的不一致性。

为什么说MySQL的Repeatable Read依然可能会有幻读的情况发生?

为什么说是可能,这个我们需要结合一定的业务场景来进行说明才好理解。

事务1 事务2
begin  
select * from t_test where id=1; begin
(empty resultSet) insert into t_test(id,name) values(1,'idea');
  commit;
insert into t_test(id,name) values(1,'idea');  
Duplicate entry '1' for key 't_test.PRIMARY'(主键冲突)  
commit;  

例如上边我所描述的场景中,我们的事务会话1里面,对于表先做了查询的判断,发现并没有任何记录,所以进行了insert操作,但是在事务1insert之前,事务2已经抢先一步进行了insert,所以出现了主键冲突的问题,这就是一个典型的幻读问题场景。

为了避免在RR隔离级别下,会再有幻读的问题发生,我们需要怎么解决呢?

使用Next-Key Lock 算法,这种算法是只有InnoDB的RR隔离级别下才会有使用到的。我们举个场景来理解下就好懂了。

select * from t_test where user_id=10001 for update;

这里user_id是唯一的辅助索引,因此在进行查询的时候会将10001行给锁住,当有其他的事务希望对10001记录进行修改,则需要等待行锁的释放。这种情况确实可以有效避免掉幻读的发生,但是是在当前读的基础上去实现的。

学新通 如果是基于快照读的话,就不能避免幻读了,例如下边这种:

select * from t_test where user_id=10001;

所以如果以后再遇到说,RR是否可以解决幻读的问题,我们应该分场景来解答,如果是快照读的话,依旧有幻读,如果是当前读,那么就可以避免。

当前读避免幻读的原理是什么

这里我直接给结论大家看:

  • 等值查询的当前读(数据存在),并且还是索引的情况
select * from t_test where user_id=10001 for update;

加入了行锁,避免外界数据对该行进行修改

学新通

  • 等值查询的当前读(数据不存在),并且还是索引的情况 假设目前我们存在10001和10005,那么执行以下sql:
select * from t_test where user_id=10003 for update;

锁住的范围是:(10001,10005),所以如果有任何的更新或者插入操作希望在10002,10003,10004之间发生的话,都会被堵塞住。

学新通

  • 索引范围查询当前读
select * from t_test where user_id>10003 and user_id<=10009 for update;

锁住的范围是(10003,10009],相当于是加入了一把间隙锁区避免幻读了。

  • 非索引的当前读
select * from t_test where name like '%ide%' for update;

会有锁表的可能

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgahhke
系列文章
更多 icon
同类精品
更多 icon
继续加载