一致性非锁定读和锁定读

一致性非锁定读

对于一致性非锁定读的实现,通常做法是加一个版本号或者时间戳字段,在更新数据的同时版本号+1或者更新时间戳。查询时,将当前可见的版本号与对应记录的版本号进行对比,如果记录的版本小于可见版本,则表示该记录可见

在InnoDB存储引擎中,多版本控制就是对非锁定读的实现。如果读取的正在执行DELETEUPDATE操作,这时读取操作不会去等待行上锁的释放。相反,InnoDB存储引擎回去读取行的一个快照数据,对于这种读历史数据的方式,我们叫快照读。

可重复度提交读两个隔离级别下,如果执行普通的select语句则会使用一致性非锁定读(MVCC)。并且在重复读下MVCC实现了可重复读和防止部分幻读

锁定读

如果执行的是下列语句,就是锁定读

  • select ... for update
  • insertupdatedelete操作

在锁定都下,读取的是数据的最新版本,这种读也被称为当前读。锁定都会对读取到的记录加锁:

  • select ... lock in share mode:对记录加S锁,其他事务也可以加S锁,如果加X锁则会被阻塞。
  • select ... for updateinsertupdatedelete:对记录加X锁,且其他食物不能加任何锁。

在一致性非锁定读下,即使读取的记录已被其他事务加X锁,这时记录也是可以被读取的,即读取的快照数据。上面说在可重复度下MVCC防止了部分幻读,这边的"部分"是指在一致性非锁定读的情况下,只能读取到第一次查询之前所插入的数据(根据Read View判断数据可见性,Read View在第一次查询时生成)。但是!如果是当前读,每次读取的都是最新数据,这时如果两次查询中间有其他事务插入数据,就会产生幻读。所以,InnoDB在实现可重复读时,如果执行的是当前读,则会对读取的记录使用Next-key Lock,来防止其他事务在间隙间插入数据

InnoDB对MVCC的实现

MVCC的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB通过数据行的DB_TRX_IDRead View来判断数据的可见性,如不可见,通过数据行的DB_ROLL_PTR找到undo log中的历史版本。每个事务读取到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务的创建Read View之前已经提交的修改和该事务本身做的修改。

隐藏字段

在内部,InnoDB存储引擎为每行数据添加了三个隐藏字段

  • DB_TRX_ID:表示最后一次插入或更新该行的事务id。此外,delete操作在内部被视为更新,只不过会在记录头Record headerdeleted_flag字段将其标记为已删除
  • DB_ROLL_PTR:回滚指针,指向该行的undo log。如果改行未被更新,则为空
  • DB_ROW_ID:如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引

Read View

Read View主要是用来做可见性判断,里面保存了"当前对本事务不可见的其他活跃事务"

undo log

undo log主要有两个作用:

  • 当事务回滚时用于将数据恢复到修改之前的样子
  • 另一个作用是MVCC,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过undo log读取之前版本的数据,以此实现非锁定读

在InnoDB存储引擎中undo log分为两种:insert undo logupdate undo log

insert undo log:指在insert操作中产生的undo log。因为insert操作的记录只对事务本身可见,对其他食物不可见,故该undo log可以在事务提交后直接删除。

update undo log:update或delete操作中产生的undo log。该undo log需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

不同事务或者相同事务对同一记录行的修改,会使该记录行的undo log称为一条链表,链首就是最新的记录,链尾就是最早的旧纪录。

数据可见性算法

在InnoDB存储引擎中,创建一个新事物后,执行每个select语句前,都会创建一个快照,快照中保存了当前数据库系统中正处于活跃(没有commit)的事务的ID号。其实简单说保存的就是系统中不应该被本事务看到的其他事务ID列表。当用户在这个事务中要读取某个记录行的时候,InnoDB会将该记录行的DB_TRX_IDRead View中的一些变量及当前事务ID进行比较,判断是否满足条件。

RC和RR隔离级别下MVCC的差异

在事务隔离级别RC和RR(InnoDB存储引擎的默认事务隔离级别)下,InnoDB存储引擎使用MVCC(非锁定一致性读),但它们生成Read View的时机不同

  • 在RC隔离级别下的每次select查询前都生成一个Read View
  • 在RR隔离级别下只在事务开始后第一次select数据前生成一个Read VIew

MVCC和Next-key-Lock防止幻读

InnoDB存储引擎在RR级别下通过MVCC和Next-key Lock来解决幻读问题:

1.执行普通select,此时会以MVCC快照读的方式读取数据

在快照读的情况下,RR隔离级别只会在事务开启后的第一次查询生成Read View,并使用直到事务提交。所以生成Read View之后其他事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的"幻读"

2.执行select…for update/lock in share mode、insert、update、delete等当前读

在当前读下,读取的都是最新的数据,如果其他事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!InnoDB使用Next-key Lock来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其他事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读