MVCC(多版本并发控制)

  1. 事务隔离的实现:每条记录在更新的时候都会同时记录一条回滚操作。
  2. 同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。

快照读


在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库的(但并非有100G就拷贝100G数据)。

  1. InnoDB 每个事务都有唯一的事务ID(transaction id),事务开始的时候向InnoDB的事务系统申请的是按申请顺序严格递增的。
  2. 每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。
  3. InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”(启动且未提交)的所有事务 ID。

当前读

  当前读是基于 临键锁(行锁 + 间歇锁)来实现的,适用于 insert,update,delete, select … for update, select … lock in share mode 语句,以及加锁了的 select 语句。
  当前读:
  更新数据时,都是先读后写,而这个读,就是当前读。读取数据时,读取该条数据的已经提交的最新的事务,生成的 readView。
  例如事务 A 有2个 sql 语句,事务开始时生成 readView(id = n),第一个 sql 操作一条数据时读当前的 readView(id = n) 。此时开始事务B生成 readView(id = n + 1),并且对该条数据做了操作(非简单 select 操作)。事务A的第2个 sql 语句当前读该数据时,就会读取该数据的最新事务视图 readView (id =n + 1) 的值。
  而假如事务A的第二个 sql 语句操作数据时,事务B还未提交(非简单 select 操作),那么该条数据此时被事务B的写锁锁住。事务A的第二个 sql 语句操作数据(非简单 select 操作),那么也要获取该条数据的锁。而此时锁被事务B持有,事务A就会阻塞,等待事务B释放锁。

活跃事务数组


活跃事务数组中事务ID最小值为低水位、最大值+1为高水位
MVCC - 图1
对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
  3. 如果落在黄色部分,那就包括两种情况:
    a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。

(详见:https://time.geekbang.org/column/article/70562)

总结


(1)每个事务都有一个事务ID,叫做transaction id(严格递增)
(2)事务在启动时,找到已提交的最大事务ID记为up_limit_id。
(3)事务在更新一条语句时,比如id=1改为了id=2.会把id=1和该行之前的row trx_id写到undo log里,
并且在数据页上把id的值改为2,并且把修改这条语句的transaction id记在该行行头
(4)再定一个规矩,一个事务要查看一条数据时,必须先用该事务的up_limit_id与该行的transaction id做比对,
如果up_limit_id>=transaction id,那么可以看.如果up_limit_idtransaction id,才返回数据
###更新数据的“当前读”(current read)。
更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。