• 事务的丢失更新


    我们都知道,Mysql Innodb引擎的默认事务隔离级别是RR可重复读,也就是在同一个事务中,多次读取相同的数据结果相同。而其底层就是通过:“排它锁+MVCC”来实现的。

    话不多说,我们来看看下面的这个问题:

    我们可以看到,上面的事务A在更新数据之前,数据已经被事务B所修改,但是事务A最终提交的时候,将事务B的提交覆盖掉了,导致了事务B的更新丢失。

    用我们之前学过的MVCC来理解一下为什么这个问题会发生:

    首先由于事务A开启的时候,事务B还没提交,此时A事务进行了一次select操作,导致生成ReadView。根据这张图的原理:

    B事务处于trx_ids的事务中,所以A事务无法看到B事务的数据处理过程,即B事务对数据的操作,对A事务不可见。这也正是可重复读的实现的原理。

    而恰恰是因为A事务看不到B事务对数据的更新,而A事务本地无论如何都是读到该数据的1000元可重复读的版本,导致A事务后面的更新操作直接在1000元这个版本上对数据添加100,将B事务的更新完全覆盖。

    那么如何解决上面的这个问题呢?

    我们来看看上面问题的本质,其实就是在事务B操作数据之前,我们就调用了select,导致生成了ReadView,而我们又拿着这个select的数据去做后面的处理,最终导致了B数据的丢失。

    看到这里可能有人会想,那等B事务的操作commit了之后再去做A事务的select操作不就不会有这个问题了吗?说的确实没错,按这么操作,B事务确实处在A事务可见区内,最终不会导致B事务的更新丢失。可是在并发情况下,你怎么知道什么时候会突然有个B事务来更新呢?

    于是我们最好的做法就是在A事务的查询时,添加一个锁,mysql中的语句如下:

    select * from demo where id = 1 for update;

    这样在A操作事务前,B事务就无法获取到锁,也就无法进行更新操作了。从而避免了更新丢失。

    其实上面的更新丢失情况,术语叫做:第二类更新丢失问题。而解决办法也有多种角度,如上面的加悲观锁方式,或者乐观锁方式(类似CAS)也可以处理此类问题。

    不过强哥还是比较喜欢上面悲观锁的方式,方便且简单有效,其他的解决办法有兴趣的同学可以去网上找找我就不在这里说明了。

    相信大家还有一个疑问,既然有第二类更新丢失问题,那么肯定也有第一类吧,没错,第一类更新丢失又叫回滚丢失:

    不过,各种数据库的各种隔离级别都不允许此类问题的产生,所以就不在这里赘述了。至于数据库是如何避免这种问题的发生,强哥也找了好多资料,可是还是没有获取到有用的原理说明,大多类似如下的解释:

    所以,在此也就不胡乱猜测,如果小伙伴们有知道的也可以留言一起讨论下哦~

  • 相关阅读:
    MySQL内连接和外连接
    MySQL 重命名数据库
    linux查看文件大小
    Linux合并两个文件夹内容
    Linux压缩和解压命令
    深度学习反向求导
    深度学习网络压缩模型方法总结
    cuda培训素材
    cuda编程-卷积优化
    交叉熵代价函数(损失函数)及其求导推导
  • 原文地址:https://www.cnblogs.com/igoodful/p/11641588.html
Copyright © 2020-2023  润新知