mysql mvvc模式,05.白话MySQL MVCC

事务特性

大家都知道关系型数据库事务的特性:ACID

935e780992ae

image

不同的隔离级别,存在着不同问题

935e780992ae

image

MySQL默认的隔离级别是RR(在生产中我们一般采用RC的隔离级别),可以解决脏读和不可重复读,但是不能解决幻读的问题,如果要解决幻读的问题,就要采用串行化的方式,这样对数据库性能会有很大影响。

今天咱们来唠唠隔离性中,MySQL InnoDB实现RC和RR的原理——MVCC

啥是MVCC?

MVCC看起来很熟悉,难道是MVC的表兄弟?开个玩笑,MVCC的英文全称是Multi Version Concurrency Control,中文是多版本并发控制。从名字看来,InnoDB是通过数据行的多个版本,来管理数据库的并发。这个数据行的版本,有点类似于我们开发代码时的多次commit版本。

通过 MVCC 我们可以解决以下几个问题:

读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

降低了死锁的概率。这是因为 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

解决一致性读的问题。一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

快照读和当前读

快照读

快照读顾名思义,就是读取的快照数据,有可能读到历史版本数据。简单的select都是快照读,比如:

select * from user_info ;

当前读

当前读就是读取最新数据,而非历史版本,加锁的select或者增、删、改,都属于当前读:

select * from user_info for update;

update user_info set name = 'zhangsan' where id = 2;

delete from user_info where id = 2;

insert into user_info values ('zhangsan');

RC和RR是如何读取数据的呢?

在读已提交和可重复读隔离级别中,多事务并发,查询的并不是当前行最新数据,而是数据库快照。

读已提交隔离级别下,会在每次查询前,读取快照,所以它是不可重复度。

可重复读隔离级别下,只会在事务开始时,查询数据库快照,所以它是可重复读。

那么问题来了,数据库快照是什么?数据库成百上千G的数据,如何能在瞬间生成快照呢?我们继续剖析。

InnoDB中MVCC如何实现的?

在深入MVCC前,我们先做一些知识准备。

事务号

InnoDB会为每个事务分配一个版本号——trx_id,这个版本号是递增的,可以以此判断事务开启的时间。

细心的同学可能会问,如果trx_id用完了怎么办,这里其实大可放心,trx_id的设计可以使用到各位开发者安享晚年,它是采用6字节无符号数据存储,最大值为2的48次方=281474976710656,如果按照每秒10wTPS,可以安全使用89年,既是用到最后一位后,便会从0开始,这样有可能会产生一些脏读,不过基本上不会发生。

行记录的隐藏字段

每个人的身上(数据行)是都有毛毛(隐藏字段)

我来给你唱(分析下)毛毛(隐藏字段)

到底我们身上都有些什么毛(隐藏字段)

我来唱(分析)给你们知道

InnoDB的叶子节点上存储的是数据页,数据页上存储着数据行,行里面存着我们定义的字段,除了我们定义的字段外,还有一些隐藏字段,用于InnoDB系统计算。

935e780992ae

image

行数据

935e780992ae

image

Undo Log

InnoDB将快照数据,记录在了Undo Log中,我们可以在回滚段中找到相关数据,如图:

935e780992ae

image

根据图来看,roll_ptr指针,将undo log用链表结构组合在一起,每个undo log都记录了当时的trx_id,这样我们可以很方便的找到历史快照。

Read View

有了这些前置条件,我们来探讨下,在RC和RR隔离级别下,InnoDB是如何通过MVCC实现一致性读的。

InnoDB中,多个事务对同一行数据更新,会产生多个历史版本(就像git

commit记录一样),这些历史版本就记录在Undo Log中,如果某个事务想要查询某行数据,那它需要读取哪行数据呢?这时候Read View登场了。

935e780992ae

image

有了这个read view,我们需要找哪个快照版本的数据,就一目了然了,那到底怎么找,我们看图,一图胜千言:

935e780992ae

image

假设当前活跃事务是trx_id_8、trx_id_10、trx_id_12、trx_id_20,那么套用变量:

up_limit_id = trx_id_8

low_limit_id = trx_id_20

trx_ids = [trx_id_8, trx_id_10, trx_id_12, trx_id_20]

假设我们在事务中需要查询的数据的trx_id为x,那么:

x < 8,表示该数据快照是当前活跃事务提交前的版本,是已经完成的事务提交的,安全可以使用。

x > 20,表示该数据快照版本是当前活跃事务之后事务提交的,是不可以使用的。

若:8 <= x <= 20

3.1 trx_ids.contain(x),该行数据未提交,不可见。

3.2 !trx_ids.contain(x),该行数据已提交,可见。

好了,那么通过这种方式,InnoDB实现了全库数据快照,并保证了高并发下数据的准确性和一致性。设计思路不得不佩服。

总结

MVCC的核心可以拆分为MV + VC,MV通过Undo Log来实现多版本的管理,VC通过Read View来实现事务并发下,数据是否可见,针对不同的隔离级别,产生Read View的时机也不一样,这样也就实现了不同的隔离级别RC和RR。

MVCC在不通的数据库下,实现的原理不一样,主要是理解设计思想。

完,谢谢大家阅读

935e780992ae

image

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值