InnoDB存储引擎MVCC的工作原理

InnoDB存储引擎的行结构

MySQL官方手册https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html

Internally, InnoDB adds three fields to each row stored in the database. A 6-byte DB_TRX_ID field indicates the transaction identifier for the last transaction that inserted or updated the row. Also, a deletion is treated internally as an update where a special bit in the row is set to mark it as deleted. Each row also contains a 7-byte DB_ROLL_PTR field called the roll pointer. The roll pointer points to an undo log record written to the rollback segment. If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated. A 6-byte DB_ROW_ID field contains a row ID that increases monotonically as new rows are inserted. If InnoDB generates a clustered index automatically, the index contains row ID values. Otherwise, the DB_ROW_ID column does not appear in any index.

上面这段就是说InnoDB存储引擎在每行记录上存有三个字段

  1. DB_TRX_ID
  2. DB_ROLL_PTR
  3. DB_ROW_ID

当然还有一个删除位。DB_TRX_ID表示最后一个事务的更新和插入。DB_ROLL_PTR指向当前记录项的undo log信息。DB_ROW_ID标识插入的新的数据行的id。

read_view:行记录的可见性

这里有必要解释一下什么是行记录的可见性,经过上文介绍可知,MVCC实现了多个并发事务更新同一行记录会时产生多个记录版本,那问题来了,新开始的事务如果要查询这行记录,应该获取到哪个版本呢?即哪个版本对这个事务是可见的。这个问题就是行记录的可见性问题。

下图是read_view_struct的结构体:

其中和可见性相关的两个变量分别low_limit_id和up_limit_id,根据注释可知,前者表示事务id大于此值的行记录都不可见,后者表示事务id小于此值的行记录都可见。具体的解释见下文。

行记录的可见性实现

生成read_view:  每个事务在开始的时候都会根据当前系统的活跃事务链表创建一个read_view,具体的创建过程:

再mysql客户端窗口中运行如下命令:删减后的重要信息如下,

mysql> show engine innodb status \G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2015-09-13 23:26:42 12cf39000 INNODB MONITOR OUTPUT
=====================================
------------
TRANSACTIONS
------------
Trx id counter 13099
Purge done for trx‘s n:o < 13097 undo n:o < 0 state: running but idle
History list length 380
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 15, OS thread handle 0x12cf39000, query id 940 localhost root init
show engine innodb status
---TRANSACTION 0, not started
MySQL thread id 14, OS thread handle 0x12cf7d000, query id 936 localhost root cleaning up
---TRANSACTION 13084, not started
MySQL thread id 3, OS thread handle 0x12ce71000, query id 837 localhost 127.0.0.1 root cleaning up
---TRANSACTION 0, not started
MySQL thread id 2, OS thread handle 0x12ce2d000, query id 863 localhost 127.0.0.1 root cleaning up
---TRANSACTION 13098, ACTIVE 19901 sec
MySQL thread id 13, OS thread handle 0x12cde9000, query id 937 localhost root cleaning up
Trx read view will not see trx with id >= 13099, sees < 13089
---TRANSACTION 13089, ACTIVE 20415 sec
MySQL thread id 12, OS thread handle 0x12cef5000, query id 938 localhost root cleaning up
Trx read view will not see trx with id >= 13090, sees < 13090
--------

1 row in set (0.00 sec)

可以看到有一个

History list length 380

这就是当前活跃的事务列表。如下所示,

ct-trx --> trx11 --> trx9 --> trx6 --> trx5 --> trx3;

ct-trx 表示当前事务的id,对应上面的read_view数据结构如下,

read_view->creator_trx_id = ct-trx;

read_view->up_limit_id = trx3;

read_view->low_limit_id = trx11;

read_view->trx_ids = [trx11, trx9, trx6, trx5, trx3];

read_view->m_trx_ids = 5;

根据以上的数据机构和行隐藏的列,还有undo log中相关的信息实现了行的可见性,具体如何实现行的可见性,如下分析

low_limit_id是“高水位”即当时活跃事务的最大id,如果读到row的db_trx_id>=low_limit_id,说明这些id在此之前的数据都没有提交,如注释中的描述,这些数据都不可见。

if (trx_id >= view->low_limit_id) {
    return(FALSE);
}

up_limit_id是“低水位”即当时活跃事务列表的最小事务id,如果row的db_trx_id<up_limit_id,说明这些数据在事务创建的id时都已经提交,如注释中的描述,这些数据均可见。

if (trx_id < view->up_limit_id) {
    return(TRUE);
}

在两个limit_id之间的我们需要从小到大逐个比较一下:

n_ids = view->n_trx_ids;
for (i = 0; i < n_ids; i++) {
    trx_id_t view_trx_id = read_view_get_nth_trx_id(view, n_ids – i – 1);
    if (trx_id <= view_trx_id) {
        return(trx_id != view_trx_id);
    }
}

这样我们在要在事务中获取数据行,我们就能根据数据行的row db_trx_id 和当前事务的read_view来判断此版本的数据在事务中是否可见。

如果数据不可见我们需要去哪里找上版本的数据呢?

就是通过刚才提到过的7BIT的DB_ROLL_PTR去undo log信息中寻找,同时再判断下这个版本的数据是否可见,以此类推。

还比如我们在上文 show engine innodb status,有如下信息,

---TRANSACTION 13098, ACTIVE 19901 sec
MySQL thread id 13, OS thread handle 0x12cde9000, query id 937 localhost root cleaning up
Trx read view will not see trx with id >= 13099, sees < 13089

will not see trx_id >= 13099 and sees <13089

就是说当前的事务ID=13098 ,此时不会看到 trx_id >= 13099 (low_limit_id)的记录,而会看到 trx_id < 13089(up_limit_id) 的记录。

还比如,

---TRANSACTION 13089, ACTIVE 20415 sec
MySQL thread id 12, OS thread handle 0x12cef5000, query id 938 localhost root cleaning up
Trx read view will not see trx with id >= 13090, sees < 13090

都是一样的道理。

现在做一个实例来推断一下(只是推断一下,毕竟又没有看mysql innodb的源码),

表结构

create table t2(
    a int primary key,
    b int not null    
);

Session A

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t2;
+----+----+
| a  | b  |
+----+----+
| 10 | 10 |
| 20 | 20 |
| 30 | 30 |
+----+----+
3 rows in set (0.00 sec)

row trx_id < read_view -> up_limit_id 可见。

Session B

read_view -> ct_trx = trx11

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t2;
+----+----+
| a  | b  |
+----+----+
| 10 | 10 |
| 20 | 20 |
| 30 | 30 |
+----+----+
3 rows in set (0.00 sec)

mysql> delete from t2 where a = 10;
Query OK, 1 row affected (0.01 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t2;
+----+----+
| a  | b  |
+----+----+
| 20 | 20 |
| 30 | 30 |
+----+----+
2 rows in set (0.00 sec)

因为执行了delete 语句,这里只读出来两条数据。

row trx_id < read_view -> up_limit_id 可见。

Session A

正如你所想的那样,如下

mysql> select * from t2;
+----+----+
| a  | b  |
+----+----+
| 10 | 10 |
| 20 | 20 |
| 30 | 30 |
+----+----+
3 rows in set (0.00 sec)

在session a事务内,都是可重复读。这里的可重复读都是根据我们上文所说的innodb可见性规则来实现的。

虽然在 session b删除了a = 10的数据行,但会有一个DB_ROLL_PTR指针指向undo log中的数据行,这个数据行的trx_id符合可见性条件,所以在session a中是可见的。

后记:根据基本的信息做推断,大概是这样的,不做过多深入的研究,大概明白原理,毕竟没有去阅读源码。

参考资料:

http://libisthanks.blog.163.com/blog/static/23527612320141016111027592/

http://www.xdata.me/?p=289

http://hedengcheng.com/?p=148

=======END=======

时间: 2024-10-11 01:02:19

InnoDB存储引擎MVCC的工作原理的相关文章

InnoDB存储引擎之Master Thread

InnoDB存储引擎的主要工作都是在一个单独的后台线程Master Thread中完成的. 1.InnoDB 1.0.x版本之前的Master Thread Master Thread具有最高的线程优先级别.其内部由多个循环组成:主循环(loop).后台循环(backgroup loop).刷新循环(flush loop).暂停循环(suspend loop).Master Thread会根据数据库运行的状态在上述4状态下进行切换.Loop被称为主循环,因为大多数的操作是在这个循环中,其中有两大

MySQL数据库InnoDB存储引擎多版本控制(MVCC)实现原理分析

文/何登成 导读:   来自网易研究院的MySQL内核技术研究人何登成,把MySQL数据库InnoDB存储引擎的多版本控制(简称:MVCC)实现原理,做了深入的研究与详细的文字图表分析,方便大家理解InnoDB存储引擎实现的多版本控制技术(简称:MVCC). 基本知识 假设对于多版本控制(MVCC)的基础知识,有所了解.MySQL数据库InnoDB存储引擎为了实现多版本的一致性读,采用的是基于回滚段的协议. 行结构 MySQL数据库InnoDB存储引擎表数据的组织方式为主键聚簇索引.由于采用索引

MySQL InnoDB 存储引擎原理浅析

注:本文主要基于MySQL 5.6以后版本编写,多数知识来着书籍<MySQL技术内幕++InnoDB存储引擎>,本文章仅记录个人认为比较重要的部分,有兴趣的可以花点时间读原书. 一.MySQL体系结构 主要包含以下几部分: 1.管理服务于工具组件. 2.连接池与鉴权. 3.SQL接口. 4.查询分析器. 5.优化器组件. 6.缓存与缓冲区. 7.各式的插件式存储引擎. 8.物理文件. 其中存储引擎是基于表,而非数据库. 二.InnoDB体系结构 InnoDB引擎包含几个重要部分: 1.后台进程

[MySQL Reference Manual]14 InnoDB存储引擎

14 InnoDB存储引擎 14 InnoDB存储引擎... 1 14.1 InnoDB说明... 5 14.1.1 InnoDB作为默认存储引擎... 5 14.1.1.1 存储引擎的趋势... 5 14.1.1.2 InnoDB变成默认存储引擎之后... 5 14.1.1.3 InnoDB表好处... 6 14.1.1.4 InnoDB表最佳实践... 6 14.1.1.5 InnoDB表提升... 6 14.1.1.6 InnoDB作为默认存储引擎测试... 6 14.1.1.7 验证In

Mysql技术内幕——InnoDB存储引擎

一.mysql体系结构和存储引擎 1.1.数据库和实例的区别 数据库:物理操作系统或其他形式文件类型的集合.在mysql下数据库文件可以是frm,myd,myi,ibd结尾的文件. 数据库实例:由数据库后台进程/线程以及一个共享内存区组成.数据库实例才是真正用来操作数据库文件的. mysql数据库是单进程多线程的程序,与sql server比较类似.也就是说,Mysql数据库实例在系统上的表现就是一个进程. 1.2.mysql的体系结构 mysql由连接池组件.管理服务和工具组件.sql接口组建

MySQL 温故而知新--Innodb存储引擎中的锁

近期碰到非常多锁问题.所以攻克了后,细致再去阅读了关于锁的书籍,整理例如以下:1,锁的种类 Innodb存储引擎实现了例如以下2种标准的行级锁: ? 共享锁(S lock),同意事务读取一行数据. ?  排它锁(X lock).同意事务删除或者更新一行数据. 当一个事务获取了行r的共享锁.那么另外一个事务也能够马上获取行r的共享锁,由于读取并未改变行r的数据.这样的情况就是锁兼容. 可是假设有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁-这样的情况就是锁不兼容.二者兼容性例如以下表

InnoDB存储引擎——后台线程

1.InnoDB存储引擎概述 InnoDB存储引擎是第一个完整支持ACID事务的MySql存储引擎,其特点是行锁设计.支持MVCC.支持外键.提供一致性非锁定读,同时被设计用来最有效地利用以及使用CPU和内存. 2.InnoDB体系结构 上图是InnoDB存储引擎的体系结构,可以看到InnoDB存储引擎有很多内存块,这些内存块组成了一个大的内存池,负责如下工作: 维护所有进程/线程需要访问的多个内部数据结构: 缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存. 重做日志

MySQL内核:InnoDB存储引擎 卷1

MySQL内核:InnoDB存储引擎卷1(MySQL领域Oracle ACE专家力作,众多MySQL Oracle ACE力捧,深入MySQL数据库内核源码分析,InnoDB内核开发与优化必备宝典) 姜承尧 蒋鸿翔 饶珑辉 温正湖 著   ISBN 978-7-121-22908-4 2014年5月出版 定价:69.00元 360页 16开 编辑推荐 预售前100位读者送MySQL 5.6 InnoDB存储引擎的架构图 l  <高性能MySQL>配套深度阅读数据库内核解析篇 l  网易资深数据

InnoDB存储引擎介绍-(2)redo和undo学习

01 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC). - 事务的原子性(Atomicity)事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作.如果在执行的过程中发生了错误,要回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过. - 原理Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数