MySQL Innodb MTR源码解析

最近看了下Mysql innodb源码MTR模块,了解源码能帮助DBA更熟悉数据库运行原理、更容易定位排查问题。那么什么是Mtr?Mtr究竟是用来做什么的?围绕几个问题我们来做一下深入研究。

一、什么是MTR?

Mtr即Mini-transaction的缩写,字面意思小事物,相对逻辑事物而言,我们把它称作物理事物。属于Innodb存储引擎的底层模块。主要用于锁和日志信息。

我们知道Innodb事物有四种特性:ACID即原子性、一致性、隔离性、持久性,通常称为逻辑事物。用来保证一致性和持久性的机制就是Mtr,即锁和日志,一旦事务提交,则其所做的修改会永久保存到数据库。

二、MTR原理

  • 查询发起后,当并发事务操作或数据库异常时不管数据是否发生了变化,查询结果应当为发起查询时间一致的数据。这就是通过Mtr保证了页的一致性,具体遵循三个原则:

1) The FIX Rules:

修改或访问一个页需要获得该页的x-latch或s-latch,直到修改或者访问该页的操作完成。每个页都有个buf_block_t的对象:

    struct buf_block_struct{
 ......
 rw_lock_t lock;    #对页的latch操作
 ulint buf_fix_count;    #有多少个操作fix该页
 ......
    }

其中lock实现对页的latch操作,buf_fix_count表示有多个操作fix该页。多个事物读取一个页时,该变量就会自增。当一个页根据LRU算法从缓冲池中替换时,该变量必须为0。

2) Write-Ahead Log:

如果一个页操作在写入到持久设备时,必须先将内存中小于该页LSN的日志写入到持久化设备中。

3) Force-log-at-commit:

前面两个主要是为了保证事物的一致性,而事物的持久性还需要这个规则进一步加强。一个事物可以涉及多个页,当事物提交时,产生所有的Mtr日志必须刷到持久设备中。通过innodb_flush_log_at_trx_commit参数控制是否必须启用该规则。

  • 在innodb中有两类日志:redo log和undo log。其中redo log是用来做数据异常恢复和数据库重启时页数据同步恢复的,redo log就是建立在Mtr基础上。数据库在执行事务时,通过Mtr产生redo log来保证事务的持久性。
  • 物理事务从名字来看是物理的,因为在innodb存储引擎中,只要是涉及到文件读取、修改等物理操作,都离不开Mtr,可以说Mtr是内存与文件之间的一个桥梁。
  • 可参考下图:

三、MTR工作方式

  • Mtr结构体初始化

物理事务既然被称为事务,那它同样有事务的开始与提交,在innodb中,物理事务的开始其实就是对物理事务的结构体mtr_t的初始化,在mtr0mtr.h文件中,其中包括下面一些成员:

这个结构体中dyn_array_t是两个动态数组:

  1. 成员state表示mtr的状态,包括三种状态;
  2. 成员memo用来存储所有这个物理事务用到(访问)的页面,这些页面都是被所属的物理事务上了锁的(读锁或者写锁,某些时候会不上锁);
  3. 成员log用来存储这个物理事务在访问修改数据页面的过程中产生的所有日志,这个日志就是数据库中经常说到的重做(redo log)日志;
  4. 成员n_log_recs表示这个物理事务操作的页个数;
  5. log_mode表示这个物理事务的日志模式,包括MTR_LOG_ALL(写日志)、MTR_LOG_NONE(不写日志)等;
  6. LSN两个成员表示这个物理事务开始前的LSN及这个物理事务提交后产生的新的LSN。
  • mtr_t中成员memo

是个latch持有状态的数组列表,采用的是dyn_array_t的动态内存结构来保存的,每个单元存储的是mtr_memo_slot_t这样的结构。定义如下:

latchtype如下:

MTR_MEMO_PAGE_S_FIX

/rw_locks-latch/

MTR_MEMO_PAGE_X_FIX

/rw_lockx-latch/

MTR_MEMO_BUF_FIX

/buf_block_t/

MTR_MEMO_S_LOCK

/rw_lock s-latch/

MTR_MEMO_X_LOCK

/rw_lock x-latch/

object是latch的对象,可以是rw_lock_t对象,也可以是buf_block_t对象。

memo的latch管理接口:

mtr_memo_push

获得一个latch,并将状态信息存入mtr memo当中

mtr_memo_slot_t

保存latch内容

mtr_release_s_latch_at_savepoint

释放memo偏移savepoint的slot锁状态

mtr_memo_contains

判断锁对象是否在memo当中

mtr_memo_slot_release

释放slot锁的控制权

mtr_memo_pop_all

释放所有memo中的锁的控制权

  • mt_t中的成员log

是也是一个dyn_array_t动态结构的内存,用来保存mtr产生的日志信息。日志的写入是通过mtr0log.h来写入的。

重做日志虽然有很多种类型,但重做的日志格式是统一的,日志格式是有日志头和日志体组成,日志头信息是由type、space和page no组成,由mlog_write_initial_log_record_fast函数写入到mtr_t的log中的。log body的数据写入是通过mtr0log.h中的日志写入方法进行写入的,每写入一条操作日志,n_log_recs会加1。

以下是一个比较具体的示意图:

  • mt_t中的成员modifications

是标识是否有page的数据改动,如果有,在mtr_commit调用时会先将mtr->log刷盘,然后释放mtr所有的所控制权。日志一定会在mtr结束时刷盘,这符合Force-log-at-commit的规则。日志写入调用的是log_write_low这个函数。

  • 其中memo和log的保存形式如下:
  • Mtr抽象流程图及步骤
  1. 首先在系统将一个页面载入到缓冲区的时候,需要新开始一个(mtr_start)或者一个已经开始的物理事务,载入时需要指定页的获取方式,比如是用来读取的还是用来修改的,这样会影响物理事务对这个页的上锁情况,如果用来修改,则上X锁,否则上S锁(当然还可以指定不上锁)。在确定了获取方式、这个页的表空间ID及页号之后,就可以通过函数buf_page_t来获取指定页面了。
  1. 当找到相应页后,物理事务就要对它上指定的锁,此时需要对这个页的上锁情况进行检查,一个页的上锁情况是在结构体buf_block_struct中的lock中体现的,此时如果这个页还没有上锁,则这个物理事务直接对其上锁,否则还需要考虑两个锁的兼容性,只有两个锁都是共享锁(S)的情况下才是可以上锁成功的,否则需要等待。当上锁成功后,物理事务会将这个页的内存结构存储到上面提到的memo动态数组中。然后这个物理事务就可以访问这个页了。mtr_start再mtr0mtr.ic文件中。
  1. 物理事务对页面的访问包括两种操作,一种是读,另一种是写,读就是简单读取其指定页面内偏移及长度的数据;写则是指定从某一偏移开始写入指定长度的新数据,同时如果这个物理事务是写日志的(MTR_LOG_ALL),此时还需要对刚才的写操作记下日志,这里的日志就逻辑事务中提到的redo日志。写下相应的日志之后,同样将其存储到上面的log动态数组中,同时要将上面结构体中的n_log_recs自增,维护这个物理事务的日志计数值。
  2. 物理事务的读写过程主要就是上面介绍的内容,其最重要的是它的提交过程。物理事务的提交是通过mtr_commit来实现的,物理事务的提交主要是将所有这个物理事务产生的日志写入到innodb的日志系统的日志缓冲区中,然后等待srv_master_thread线程定时将日志系统的日志缓冲区中的日志数据刷到日志文件中。

四、确保MTR完整性

上面已经提过,物理事务和逻辑事务一样,也是可以保证数据库操作的完整性的,一般说来,一个操作必须要在一个物理事务中完成,也就是指要么这个操作已经完成,要么什么也没有做,否则有可能造成数据不完整的问题,因为在数据库系统做redo操作时是以一个物理事务为单位做的,如果一个物理事务的日志是不完整的,则它对应的所有日志都不会重做。那么如何辨别一个物理事务是否完整呢?

这个问题是在物理事务提交时用了个很巧妙的方法保证了,在提交前,如果发现这个物理事务有日志,则在日志最后再写一些特殊的日志,这些特殊的日志就是一个物理事务结束的标志,那么提交时一起将这些特殊的日志写入,在重做时如果当前这一批日志信息最后面存在这个标志,则说明这些日志是完整的,否则就是不完整的,则不会重做。

物理事务提交时还有一项很重要的工作就是处理上面结构体中动态数组memo中的内容,现在都已经知道这个数组中存储的是这个物理事务所有访问过的页面,并且都已经上了锁,那么在它提交时,如果发现这些页面中有已经被修改过的,则这些页面就成为了脏页,这些脏页需要被加入到innodb的buffer缓冲区中的更新链表中(详细了解BUFFER),当然如果已经在更新链中,则直接跳过(不能重复加入),svr_master_thread线程会定时检查这个链表,将一定数目的脏页刷到磁盘中,加入之后还需要将这个页面上的锁释放掉,表示这个页面已经处理完成;如果页面没有被修改,或者只是用来读取数据的,则只需要直接将其共享锁(S锁)释放掉即可。

五、总结

上面的内容就是Mtr的一个完整的讲述,因为它是比较底层的一个模块,牵扯的东西比较多,这里重点讲述了物理事务的意义、操作原理、与BUFFER系统的关联、日志的产生等内容。

Mtr是innodb对ACID中的持久性的最小保证单元,所有涉及到事务执行、页数据刷盘、redo log数据恢复等都需要进行mini transaction的构造和执行。几乎所有的模块都涉及到minitransaction,例如:btree、page、事务、inser tbuffer、redo-log等,对mini transcaion的理解不能孤立的去看源码,应该结合redo log、page等相关的代码了解。它是理解innodb工作原理的基石。

六、MTR涉及的模块文件

.h头文件统一放在include目录下,全局变量定义,宏定义、函数说明等

.ic也在include目录下,这类文件为每个模块定义了内联函数

.c\.cc标准源文件,在各自模块目录下,具体实现功能

mtr0mtr.h

mtr0mtr.ic

page0cur.c

mtr0log.h

mtr0log.ic

mtr0mtr.cc

mtr0types.h

mtr0log.cc

mem0mem.h

dyn0dyn.h

buf0types.h

sync0rw.h

ut0byte.h

page0types.h

dict0types.h

buf0buf.h

mach0data.h

参考:《MYSQL内核:INNODB存储引擎》

原文发布于微信公众号 - MYSQL轻松学(learnmysql)

原文发表时间:2017-03-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

基于Innobackupex的不完全恢复

    对于MySQL的不完全恢复,我们可以借助于Innobackupex的多重备份加上binlog来将数据库恢复到任意时刻。这里的不完全恢复(也叫时点恢复)...

541
来自专栏SAP最佳业务实践

SAP最佳业务实践:MM–交货与库存调拨(134)-6收货

4.8 MB5T检查库存调拨状态 该活动执行实际在途库存的检查。 后勤-物料管理-库存管理-环境-库存-在途库存 1. 在 显示在途库存 屏幕中,进行以下...

3155
来自专栏运维小白

10.3 top命令

监控系统状态 top 命令,查看进程使用资源情况 top -c 显示详细的进程信息 top -bn1 静态显示所有进程 q 键退出,数字1显示所有核cpu,大写...

1695
来自专栏散尽浮华

Mysql优化系列(1)--Innodb引擎下mysql自身配置优化

1.简单介绍 InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句提供一...

3245
来自专栏移动开发的那些事儿

Android Sqlite并发问题

如上异常堆栈中的错误信息error code 5: database is locked,经过查找发现code为5代表sqlite中的SQLITE_BUSY异常...

744
来自专栏更流畅、简洁的软件开发方式

很简单的企业管理器---我写程序的方式,几个自定义控件。

这里呢我利用我常用的东东写个实例,抛砖引玉,大家也都来批批,帮助我提高嘛。 我常用的呢是 数据访问层(简单理解是SQLHelp,但是绝不等于)、分页控件等自定...

1878
来自专栏Java学习123

WebSphere MQ基础命令

3086
来自专栏jouypub

Hive日常操作

根据分区查询数据:select table_coulm from table_name where partition_name = '2018-11-01';

832
来自专栏Java编程技术

使用MySQL Server Side Cursor解决查询数据量过大造成OOM

前面介绍了MyBaits中两种使用游标的方式来避免搜查内容过大导致OOM,这两种方式被称为是客户端side的游标,因为mysql client每次从自己的接受b...

761
来自专栏闵开慧

mysql性能调优

mysql调优思路: 1.数据库设计与规划--以后再修该很麻烦,估计数据量,使用什么存储引擎  2.数据的应用--怎样取数据,sql语句的优化  3.mysql...

3165

扫码关注云+社区