MySQL-MVCC、当前读、快照读

MVCC

多版本并发控制,通过保存数据在每个时间点的快照来实现。

InnoDB的MVCC通过在每行记录后面保存两个隐藏的列来实现,一列保存行的创建时间,一列保存行的删除时间,这个时间指的是系统版本号。

在这里插入图片描述
在这里插入图片描述

MVCC只在读已提交(RC)和可重复读(RR)两个隔离级别下工作。

快照读

读取记录的可见版本(有可能是历史版本),不用加锁。

简单的select操作,不包括 select ... lock in share modeselect ... for update

  • RC:每次select都生成一个快照读,总是读取已提交的最新数据行

  • RR:第一次select生成快照读,读取上一次的快照

举例: 在这里插入图片描述 在这里插入图片描述

对于操作的前四行,即只有事务2已提交

  • 读已提交: 未提交的事务id:[1,3] 最大的事务id:3 每一次查询都生成一个快照读,所以事务4的查询结果为29。

  • 可重复读: RR第一次查询会生成快照,对一个事务,每次都沿用第一次的快照,直接复制一份之前的快照。 则在RR下,事务4再次查询的结果将会是29,而不是图中RC情况下的30。

当前读

读取的是最新版本,并且对读取的记录加锁。

select...lock in share mode (共享读锁) select...for update update , delete , insert

上面形式都属于当前读。

当前读的实现方式

使用next-key锁(行记录锁+Gap间隙锁)实现

RR级别怎样防止幻读?

RR级别下只要对 SELECT 操作也手动加行(X)锁即可类似 SERIALIZABLE 级别(它会对 SELECT 隐式加锁)

这里需要用 X锁, 用 LOCK IN SHARE MODE 拿到 S锁 后我们没办法做 写操作
SELECT `id` FROM `users` WHERE `id` = 1 FOR UPDATE;
如果 id = 1 的记录存在则会被加行(X)锁;如果不存在,则会加 next-key lock/ gap 锁(范围行锁),即记录存在与否,mysql 都会对记录对应的索引加锁,其他事务是无法操作的。 在这里插入图片描述 如图,表t2有主键,score字段有索引idc_scaore,在事务中查询t2表score<80的记录,加了一个S锁(lock in share mode)。 在另一个事务中插入score=74,无法插入成功,因为有gap锁;插入score=90成功,因为不在此区间内。 在这里插入图片描述

------ 本文结束感谢您的阅读 ------