原文链接:Understanding InnoDB MVCC

多版本并发控制 (MVCC) 是一种能使关系性数据支持并发,或多个用户同时访问数据库数据的设计理念。

在 Mysql 的 InnoDB 存储引擎提供了 MVCC, 行级锁,ACID 以及其他一些功能。

我对该数据库理论的理解为,在 MVCC 控制下,应该完全支持访问修改(例如 UPDATE)部分特殊数据。然而在 InnoDB 引擎下,我觉得是通过一定程度的互斥锁来实现的。

为了理清记录这些情况,我去寻求 InnoDB 内部专家的一件像 Mark Callaghan,Percona 以及 InnoDB 的开发团队。我很乐意承认我不是一个精通 Mysql方方面面的专家,特别是我还没有详细阅读源码已经明白内部工作原理。

情况说明

单表通过主键更新一定范围的行会被其他在同一张表类似的操作所阻塞,但每个查询的数据集实际上是唯一的。

问题描述

$ mysql -u -p test
drop table if exists numbers;
create table numbers (id int unsigned not null primary key, f1 int not null, f2 int not null) engine=innodb;

delimiter $$

drop procedure if exists fill_numbers $$
create procedure fill_numbers(in p_max int)
deterministic
begin
  declare counter int default 1;
  truncate table numbers;
  insert into numbers values (1,1,1);
  while counter < p_max
  do
      insert into numbers (id,f1, f2)
          select id + counter, counter + f1, id - f2
          from numbers;
      select count(*) into counter from numbers;
      select counter;
  end while;
end $$
delimiter ;

call fill_numbers(2000000);

我开了两个独立的线程在不同的主键范围内执行相同的语句。

--thread 1
start transaction;
update numbers
set f2 = f2 +200
where id between 1 and 1000000;
commit;

--thread 2
start transaction;
update numbers
set f2 = f2 +300
where id between 1000001 and 2000000;
commit;

在第三个线程监控一下 InnoDB 内部的事务。

-- thread 3
show engine innodb statusG

在更新过程中,看到了以下的错误。

---TRANSACTION 0 7741, ACTIVE 20 sec, process no 2159, OS thread id 1188534592 fetching rows, thread declared inside InnoDB 275
mysql tables in use 1, locked 1
2007 lock struct(s), heap size 292848, 1001862 row lock(s), undo log entries 999858
MySQL thread id 918563, query id 16802707 localhost root Updating
update numbers set f2 = f2 +300 where id between 1000001 and 2000000
---TRANSACTION 0 7740, ACTIVE 21 sec, process no 2159, OS thread id 1178949952 fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 2008 lock struct(s), heap size 292848, 1002005 row lock(s), undo log entries 1000000
MySQL thread id 918564, query id 16802694 localhost root Updating
update numbers set f2 = f2 +200 where id between 1 and 1000000
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 16052 n bits 568 index `PRIMARY` of table `test`.`numbers` trx id 0 7740 lock_mode X waiting
Record lock, heap no 256 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 000f4241; asc   BA;; 1: len 6; hex 000000001e3d; asc      =;; 2: len 7; hex 00000033630110; asc    3c  ;; 3: len 4; hex 800f4241; asc   BA;; 4: len 4; hex 80050584; asc     ;;

这个错误在 MySQL 多个不同的版本包括硬件平台被重现了,5.0.67, 5.0.81 and 5.1.25。

导致这个问题的原因是什么?

  • 是一个 bug ? 不是。
  • 我理解的 MVCC 理论是错误的?有可能。
  • 在 InnoDB 引擎下 MVCC 没有完全实现?不是,Heikki 和 他的团队比大多数的数据库专家对数据理论有更深入的理解
  • 是 Mysql 的内核干扰了 InnoDB 存储引擎?不是,