MVCC机制

MVCC

1. MVCC是什么?

MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。 MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView

2. 当前读和快照读

  • 当前读

当前读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:

select * from test lock in share mode; # (共掌锁)
select* from test for update; # (排他锁)
# update、insert、delete等DML语言也会加排它锁,造成当前读
  • 快照读

    • Repeatable Read(可重复度隔离级别):开启事务后第一个selecti语句才是快照读的地方。

    • Read Committed(读提交隔离级别):每次select,都生成一个快照读。

简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

3.MVCC实现原理

对于每一个数据库中的数据都会创建 3 个隐藏字段,包括 db_trx_id、db_roll_pointer、db_row_id。其中 db_trx_id 代表的是本次事务的id,db_roll_pointer 代表的是上一个已提交事务的地址,db_row_id 只有当该表不存在主键时才会创建。

在这里插入图片描述

undo log 调用链,undo log 会保存每一次对数据库的修改,用来保证输事务回滚。其中链表的尾部代表的是最新的修改,链表的首部代表的是最早的旧记录。

其中由于 select 不会对数据进行修改,所以 select 不会添加 undo log 日志。只有进行 DML 操作的时候才会有 undo log

  • Insert :插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。

  • Update :修改一条记录时,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。

  • Delete :删除一条记录时,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。 删除操作的时候,会有一个逻辑删除的字段 DELETED_BIT,将其置为 true 并不是真正的删除

为了节省磁盘空间,InnoDB有专门的purge线程来清理DELETED_BIT为true的记录。为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view);如果某个记录的DELETED_BIT为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。

在这里插入图片描述

4. readView

InnoDB的默认隔离级别是可重复读,所以在该事务之前且提交的数据改变是可见的,该事务之后的数据改变不可见。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MVLDfcJI-1669391753167)(images/image-20221125225734637.png)]

如图所示,如果该线程开启的事务 id 为 20,那么在 20 之前且提交的事务都是可见的,在 20 之后的事务都是不可见的。不过MySQL进行优化,用一个 readView 来存放当前的活跃事务。

在这里插入图片描述

如上图所示,事务 20,对于事务 7 以前的都是可见的,但是对于事务 7 、事务 9 和事务 11 是不可见的,因为他们是活跃的事务,还没有提交。对于事务20以后是不可见的,因为肯定会大于 20 。

5. 可重复读原理

可重复读就是利用了这样的一个思想,当进行第一次快照时,变回构建视图。

在这里插入图片描述

如上图,当开启事务 20 视图列表就会构建好。但是进行当前读的会重新构建视图。

思考

事务 9 对表中字段进行修改当前读能看到吗?如果能看到,那岂不是违背了可重复读?

答:在进行第一次快照读建立视图之后,以后所有快照读都会使用这个视图。而当前读是可以看到之前提交的内容。下面是我做的一个实验,可以看出很容易出现莫名其妙的BUG,比如我只想加 1 ,但是最后却加了 2 。如果此时采用 where 条件去筛选很可能会发生意料之外的错误。

事务1事务2
begin;
begin;
select * from product; # (price = 5000)
update product set price = price+1 where pid=1;
commit;
select * from product lock in share mode; # (price = 5001)
select * from product; # (price = 5000)
update product set price = price+1 where pid=1;
select * from product; # (price = 5002) 自己的修改是可见的

6. 读提交

了解了可重复读之后,读提交其实比较容易理解,就是每一次select之后都进行一次视图重建。下面是采用读提交的过程。

事务1事务2
begin;
begin;
select * from product; # (price = 5000)
update product set price = price+1 where pid=1;
commit;
select * from product lock in share mode; # (price = 5001)
select * from product; # (price = 5001)
update product set price = price+1 where pid=1;
select * from product; # (price = 5002) 自己的修改是可见的

引用

https://pdai.tech/md/db/sql-mysql/sql-mysql-mvcc.html#%E4%BB%80%E4%B9%88%E6%98%AFmvcc
《MySQL实战45讲》
https://www.bilibili.com/video/BV1Kr4y1i7ru

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值