1
MVCC:多版本并发控制,不加锁就让多个事务并发读写,提高并发的读写性能

当前读&&快照读??

  • 当前读:就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

    像 select .....lock in share mode (共享锁),

    select ..... for update (排他锁) ; 

    update, insert ,delete (排他锁)

    这些操作都是一种当前读。当前读实际上是一种加锁的操作,是悲观锁的实现。

  • 快照读:快照读的实现是基于多版本并发控制,既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;
    像不加锁的select * from 操作就是快照读,即不加锁的非阻塞读,不涉及其他锁之间的冲突;

事务ID

当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大


MVCC模型在MySQL中的具体实现原理:

1.是由  3个隐式字段,undo日志 ,Read View 等去完成的

2.旧数据存储在UNDO中,再通过DB_ROLL_PTR 回溯查找历史版本

3.通过read view判断行记录是否可见

隐藏字段

每行记录除了我们自定义的字段外,还有数据库隐式定义的字段

        DB_TRX_ID

       6byte,事务ID:记录创建这条记录/最后一次修改该记录的事务ID

        DB_ROLL_PTR

        7byte,回滚指针,指向这条记录的上一个版本

        DB_ROW_ID

        6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB\_ROW\_ID产生一个聚簇索引

例如下图:

image.png
(新insert记录的DB_ROLL_PTR指针为NULL。修改新值后,记录的 DB_ROLL_PTR 回滚指针指向原始值在Undo Log 日志的位置)

以一条SQL为例子:
image.png

读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:read view (一致性视图)

    § RR隔离级别下,在每个事务开始的时候,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)。

    § RC隔离级别下,在事务中的每个语句开始(select)时,会将当前系统中的所有的活跃事务拷贝到一个列表中(read view)

然后按照以下逻辑判断事务的可见性
image.png

如果上面的还是没有看懂,可以参看下面的逻辑图:
image.png
image.png

如果查询出来的结果是,数据不可见,那么就需要去undo log中查找最新的已提交的数据,通过undolog 中记录的事务id 和 read view做比较,不在readview且最大的事务id就是满足条件的数据了。
image.png

注意:事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。

事务A
image.png
事务B
image.png

当执行事务A时,等到第2步执行完毕后,执行事务B,再接着执行事务A,发现数据update失败,就是应为每次更新数据都是当前读。


总结:
(1)说白了MVCC就是为了实现 读(select)-写冲突 不加锁,而这个读指的就是快照读。在读(select)操作时不用阻塞写操作,写操作也不用阻塞读操作。

(2)MVCC只在 可重读 和 读已提交 两个隔离级别下工作,其他两个隔离级别都和MVCC不兼容。因为读未提交总是读取最新的数据行,而不是符合当前事务版本的数据行,而串行化则会对所有读取的行都加锁。

(3)事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。


阿阿阿黄
34 声望4 粉丝