mysql MVCC不能避免幻读

幻读

并发情况下,A事务读取了一条记录,此时B事务插入一条记录,A事务又读取,读到了两条数据,此时就造成了读取数据不一致,一般到这幻读通常说的是事务提交了,而且是指删除、插入带来的问题。

问题

mysql MVCC 多版本并发控制中我们介绍了什么是MVCC,MVCC会为每个事务生成一个某个时间点的快照数据,保证事务内可以避免每次读数据不一致的问题,是RC、RR隔离级别下用于提高数据库性能,避免频繁锁,读写分离实现的一种方式,既然读快照信息,那么是不是MVCC就可以避免幻读,因为事务已经有了快照数据,如果这样,那为啥RR、RC 隔离级别不能避免幻读?这个问题也有很多讨论,包括知乎上也有很多答案,本文也做一些思考和讨论,参考的文档在本文下方参考博客上。

快照读:snapshot read

MVCC针对每个事务都有一个快照数据,每次都从快照数据库读取,如果事务A内有这么一个逻辑:

  • if sex =‘男’ 就执行sql:
update tb_user set type = 1 where id = 8;

因为每次都是快照读,那么每次读肯定都是男,如果在事务没commit 之前,事务B 执行结束了,它把sex改为了‘女‘,此时,你的条件不满足,但你因为读取快照信息,你也无法发现问题,所以你还是执行了sql,此时此刻,似乎也没有问题,但总体来说,这是错误的。所以当事务逻辑中有update的sql时,快照读是有问题的。

当前读:locking read

当前读是读取最新数据的一种读方式,不会从快照数据里读取,这样就可以读取到其他事务已经提交的数据,是特殊的读操作,插入/更新/删除操作属于当前读,需要加锁。

  • select * from table where ? lock in share mode; (加S锁)
  • select * from table where ? for update;(加X锁)
  • insert into table values (…);(加X锁)
  • update table set ? where ?;(加X锁)
  • delete from table where ?;(加X锁)

所以MVCC为了解决快照读的问题引入了当前读,innodb 引擎实际执行的是当前读,但如果使用select for update,当前读就能看到每次读的数据就不一样了,就引起了幻读,你能看到其他事务insert的数据。

总结:快照读是没有幻读存在的,当前读才会出现幻读

幻读产生

select不会有phantom,读的是快照;可对于select for update(locking read)这种语句,被认为是写,如果:

  • 事务A:select读快照(non-locking read)
  • 事务B:insert 一条记录
  • 事务A:select for update(locking read)

幻读就产生了,事务A两次读,数据数量变了。

如何解决幻读

很明显可重复读的隔离级别没有办法彻底的解决幻读的问题,如果我们的项目中需要解决幻读的话也有两个办法:

  • 使用串行化读的隔离级别
  • MVCC+next-key locks:next-key locks由record locks(索引加锁) 和 gap locks(间隙锁,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据)

参考博客

MySQL的可重复读级别能解决幻读吗
既然MySQL中InnoDB使用MVCC,为什么REPEATABLE-READ不能消除幻读?
mysql在RR的隔离级别下,究竟是通过MVCC解决幻读的还是通过行锁的next key算法解决的?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值