MVCC

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

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

mysql什么时候开始有while mysql什么时候当前读_新版本


mysql什么时候开始有while mysql什么时候当前读_加锁_02

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

快照读

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

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

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

举例:

mysql什么时候开始有while mysql什么时候当前读_mysql什么时候开始有while_03


mysql什么时候开始有while mysql什么时候当前读_隔离级别_04

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

  • 读已提交:
    未提交的事务id:[1,3]
    最大的事务id:3
    每一次查询都生成一个快照读,所以事务4的查询结果为29。
  • 可重复读:
    RR第一次查询会生成快照,对一个事务,每次都沿用第一次的快照,直接复制一份之前的快照。
    则在RR下,事务4再次查询的结果将会是29,而不是图中RC情况下的30。

当前读

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

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

上面形式都属于当前读。

当前读的实现方式

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

mysql什么时候开始有while mysql什么时候当前读_mysql什么时候开始有while_05

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 都会对记录对应的索引加锁,其他事务是无法操作的。

mysql什么时候开始有while mysql什么时候当前读_mysql什么时候开始有while_06


如图,表t2有主键,score字段有索引idc_scaore,在事务中查询t2表score<80的记录,加了一个S锁(lock in share mode)。

在另一个事务中插入score=74,无法插入成功,因为有gap锁;插入score=90成功,因为不在此区间内。

mysql什么时候开始有while mysql什么时候当前读_隔离级别_07