InnoDB MVCC

  InnoDB 的 MVCC, 其实是通过 undo log 来实现的, 可以理解为是通过在每行记录后面保存两个隐藏的列来实现的, 分别保存了这个行的创建时间, 一个保存的是行的删除时间. 这里存储的并不是实际的时间值, 而是系统版本号(可以理解为事物的 Id), 每开始一个新的事物, 系统版本号就会自动递增, 事物开始时刻的版本号会作为事物 Id.

  对应在数据中的表如下(后面两列是隐藏列, 我们通过查询语句并看不到)

MySQL innodb源码 mysql innodb mvcc_未定义

 

   SELECT

  InnoDB 会根据以下两个条件检查每行记录:

    1. InnoDB 只会查找版本早于当前事务版本的数据行(也就是 行 的系统版本号小于或等于事物的系统版本号), 这样可以确保事物读取的行, 要么是在事物开始前已经存在的, 要么是事物自身插入或者修改过的.

    2. 行 的删除版本要么未定义, 要么大于当前事务版本号( 这可以确保事物读取到的行, 在事物开始之前未被删除)

只有条件 1, 2 同时满足的记录, 才能返回作为查询结果

   DELETE

  InnoDB 会为删除的每一行保存当前系统的版本号( 事物ID) 作为删除标识  

  看下面的具体例子分析: 第二个事物, Id为 2:

start transaction; 
select * from yang; 
select * from yang; 
commit;

   假设1:

    假设在执行这个事物 Id 为2的过程中, 刚执行到 (1), 这时, 有另一个事物 Id 为 3 往这个表里插入了一条数据; 这个事物 Id 为 3

start transaction;
insert into yang values(NULL,'tian');
commit;

     这时表中的数据如下:

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

undefined

2

long

1

undefined

3

fei

1

undefined

4

tian

3

 

     然后接着执行事物 2 中的 (2), 由于 id = 4 的数据的创建时间(事物 Id = 3), 执行当前事物的 Id 为 2, 而 InnoDB 只会查找事物 Id 小于等于当前事务 Id的数据行, 所以 id = 4 的数据行并不会在执行事务 2 中的 (2) 被检索出来, 在事物 2 中的两条 select 语句检索出来的数据如下

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

undefined

2

long

1

undefined

3

fei

1

undefined

   假设2

    假设在执行这个事物 ID 为 2 的过程中, 刚执行到 (1), 假设事物执行完事物 3 后, 接着有执行了事物 4;

start transaction; 
delete from yang where id=1; 
commit;

   此时数据库中的表如下

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

4

2

long

1

undefined

3

fei

1

undefined

4

tian

3

undefined

 

    接着执行事务 ID 为 2 的事物 (2), 根据 SELECT 检索条件可以知道, 它会检索创建时间( 创建事物的ID) 小于当前事物 ID 的行和删除事物的 ID 大于当前事务的行, 而 Id = 4 的行上面已经说过了, 而 Id = 1 的行由于删除时间 ( 删除事物 Id) 大于当前事务的 ID, 所以事物 2 的 (2) select * from yang 也会把 id = 1 的数据检索出来, 所以, 事物 2 中的两条 select 语句检索出来的数据都如下:

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

4

2

long

1

undefined

3

fei

1

undefined

  UPDATE

    InnoDB 执行 UPDATE, 实际上是新插入了一行记录, 并保存其创建时间为当前事物的 ID, 同时保存当前事务 ID 到要 UPDATE 的行的删除时间.

  假设3:

    假设在执行完事物 2 的 (1) 后又执行, 其他用户执行了事物 3, 4, 这时, 又有一个用户对这张表执行了 UPDATE 操作:

  第 5 个事物:

start transaction; 
update yang set name='Long' where id=2;
commit;

      根据 UPDATE 的更新原则: 会生成新的一行, 并在原来要修改的列的删除时间上添加本事物 ID, 得到表如下:

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

4

2

long

1

5

3

fei

1

undefined

4

tian

3

undefined

2

Long

5

undefined

   继续执行事物 2 的(2), 根据 select 语句的检查条件, 得到下表:

id

name

创建时间(事务ID)

删除时间(事务ID)

1

yang

1

4

2

long

1

5

3

fei

1

undefined

  还是和事物 2 中 (1) select 得到相同的结果.