mysql隔离级别和mvcc_数据库MVCC和隔离级别的关系是什么?

谢邀。

首先我要指出问题中提出的各种概念非常混乱。简单梳理下:

早期数据库不论读取还是写入,都用锁来实现。但是锁会带来性能的问题。人们尝试各种优化方案。写入和读取的优化方式不同。

对于数据库写入操作,没有特别好的办法,因为无论如何要避免并发修改一个数据,就得靠锁。不同的数据库对于写入操作都会加悲观锁(比如MySQL是X锁)。为了避免X锁带来的性能问题,人们在合适的场合会选择用乐观锁来优化。有的数据库内建乐观锁,但是有的没有(比如MySQL就没有),所以需要开发人员自己在数据表里加version列,自己写业务代码实现。顺便提一句,乐观锁并不一定总是比悲观锁性能表现更好,这要看竞争的程度。如果数据访问竞争的非常厉害,乐观锁只会让CPU和IO白白浪费而已。

对于读取,优化就是MVCC。现在主流的商业数据库都是基于MVCC,如MySQL InnoDB和Postgres。MVCC的意思用简单的话讲就是对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁。这样读某一个数据时,事务可以根据隔离级别选择要读取哪个版本的数据。过程中完全不需要加锁。

这样,实现两个隔离级别就非常容易:Read Committed - 一个事务读取数据时总是读这个数据最近一次被commit的版本

Repeatable Read - 一个事务读取数据时总是读取当前事务开始之前最后一次被commit的版本(所以底层实现时需要比较当前事务和数据被commit的版本号)。

举个简单的例子:一个事务A(txnId=100)修改了数据X,使得X=1,并且commit了

另外一个事务B(txnId=101)开始尝试读取X,但是还X=1。但B没有提交。

第三个事务C(txnId=102)修改了数据X,使得X=2。并且提交了

事务B又一次读取了X。这时如果事务B是Read Committed。那么就读取X的最新commit的版本,也就是X=2

如果事务B是Repeatable Read。那么读取的就是当前事务(txnId=101)之前X的最新版本,也就是X被txnId=100提交的版本,即X=1。

注意,这里B不论是Read Committed,还是Repeatable Read,都不会被锁,都能立刻拿到结果。这也就是MVCC存在的意义。

在基于MVCC的数据库实现中,根本就不需要出现Read Uncommitted这种情况。Read Uncommitted是早期数据库,读写都基于锁进行实现的产物。在实际业务中Read Uncommitted毫无意义(如果真有意义,你咋不去用NoSQL数据库?)因此:对于Postgres,Read Committed和Read Uncommitted是一样的。

对于Oracle,压根就没有Read Uncommitted这个级别。

所以从实际的角度出发,我想所有人都忘记有“Read Uncommitted”这件事。

MVCC并不是万灵药。大量的业务问题的关键点在于,你在提交一个事务那一刹那,你提交事务的所有修改依赖的读取是否都还有效。对于这种场景,无论是Read Committed还是Repeatable Read都没有什么卵用。比如扣库存就是这样典型的业务场景。

在这种场景下在MySQL InnoDB,使用者会使用select ... for update手工加锁。或者干脆用Serializable隔离级别。

在Postgres,Postgres的Repeatable Read在提交时会提供一个“提交的修改的依赖是否被修改“的检测(好绕口,但就是这个意思)。如果依赖已经被改掉了,当前事务提交一定会失败。

希望上面的简单回答可以让你重新对数据库、锁和隔离级别有所认识。如果没看懂也不奇怪,问题本身的确非常复杂,所以我专门写了一篇很长的文字来解释这个问题,希望可以帮到你。

数据库事务、隔离级别和锁​www.jianshu.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值