InnoDB数据的三种log类型和作用

undolog

undolog是InnoDB的日志,又称撤销日志文件,属于逻辑日志。undolog内存数据存储在buffer pool中,磁盘数据则存储在undo tablespace。

undolog保存类型为FIL_PAGE_UNDO_LOG在undo page中,一个undo page可以保存多条undolog记录。每条undolog记录包含该undolog在undo page的页内地址、undolog对应的记录所在的tableId(tableId全局唯一)、undolog类型、undolog编号、下一条undolog的地址、old_trx_id、old_roll_pointer、主键的每个列占用的存储空间大小和真实值、被修改字段的修改前后信息等。

undolog提供回滚和多个行版本控制(MVCC)的两个能力,保证了事务的原子性:

  • 回滚:undolog分为3类,包括TRX_UNDO_INSERT_REC、TRX_UNDO_DEL_MARK_REC、TRX_UNDO_UPD_EXIST_REC,分别对应增、删、改操作。上文讲到每一行数据有两个隐藏字段trx_id和roll_pointer,它们主要作用于数据库的事务。所谓的事务就是指对一个或多个数据库的一系列操作,这些操作需保证ACID的规则。当某个事务执行过程中对某个表执行了增、删、改操作,InnoDB会给该事务分配一个递增的独一无二的trx_id(全局变量,每增加256时会刷盘),而且会生成对应的undolog,而roll_pointer就是一个指向记录对应的undolog的一个指针,数据行会存储最近提交的trx_id和roll_pointer。事务开启后,在本事务或其他事务(根据事务隔离级别)对该数据行的一次次修改中,生成的undolog会记录old_trx_id(修改该记录的上一次的trx_id)和old_roll_pointer(对应undolog地址),这样就生成了一个版本链,当需要回滚时就能沿着old_trx_id和old_roll_pointer找到一条记录的所有历史版本,从而实现事务的回滚能力。
  • MVCC:InnoDB复用了undolog中已经记录的历史版本数据来实现MVCC机制。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undolog读取之前的行版本信息,以此实现非锁定读取。此外,根据trx_id是递增的特性,InnoDB还引入了ReadView机制,用于保存创建事务时的活跃trx_id。ReadView有三个属性,分别是m_ids(活跃的trx_id列表)、min_trx_id(活跃的最小trx_id)、max_trx_id(下一个该分配的trx_id),基于这三个属性,实现了READ COMMITTED和REPEATABLE READ两种隔离级别。

因为一个事务可能包含多个增、删、改操作,为了提高并发执行多个事务写入undolog的性能,InnoDB将各个事务的各种操作通过上文提到的undo segment分开存储(undo segment的undo page通过链式存储,即每个事务都有自己的insert undo链表、update undo链表),而每个段的第一个undo page通过TRX_UNDO_STATE属性存储了该段的一些事务信息,取值有下面几个:

  • TRX_UNDO_ACTIVE: 活跃状态,即一个活跃的事务正在往这个段里边写入undolog。
  • TRX_UNDO_CACHED:被缓存的状态,即该状态下的段等待着之后被其他事务重用。
  • TRX_UNDO_TO_FREE: 可以释放,对于insert undo链表来说,如果在它对应的事务提交之后,该链表不能被重用,那么就会处于这种状态。
  • TRX_UNDO_TO_PURGE: 可以清理,对于update undo链表来说,如果在它对应的事务提交之后,该链表不能被重用,那么就会处于这种状态。
  • TRX_UNDO_PREPARED: 准备状态,还未提交。

在事务未提交前TRX_UNDO_STATE是TRX_UNDO_PREPARED状态,事务提交后,根据不同的操作类型转换成TRX_UNDO_CACHED、TRX_UNDO_TO_FREE或者TRX_UNDO_TO_PURGE状态,表示满足一定条件后可以清理这些undolog,事务如果需要回滚的话,必须是TRX_UNDO_ACTIVE或者TRX_UNDO_PREPARED状态,故事务的提交是由该属性判断的,详情见下文的事务执行流程。

redolog

redolog是InnoDB存储引擎层的日志,又称重做日志文件,属于物理日志。redolog内存数据存储在log buffer中,磁盘数据则存储在以ib_logfile0、ib_logfile1…命名的日志文件中。

上文提到,InnoDB通过buffer pool(包括change buffer、undolog)提高读写性能,但如果进程或机器崩溃会导致缓存丢失,为了能实现故障恢复就引入了redolog。事务在执行过程中对数据库所做的所有修改(聚集索引、二级索引、undolog等修改)都会生成对应的redolog,并保证redolog早于缓存落盘(WAL机制),当故障发生后,InnoDB会在重启时,通过重放redolog来恢复所做的修改。

到MySQL8.0为止,为了应对各种各样不同的需求,InnoDB已经有多达65种(上限127种)的redolog类型用来记录各种信息,而恢复数据时需要判断不同的类型,来做对应的解析。redolog长度是动态的,常见的数据结构包括日志类型、Space ID、页号、数据页中的偏移量、修改的长度和具体的值。

图片[1]-InnoDB数据的三种log类型和作用-不念博客

根据redolog不同的作用对象,可以将这些类型划分为三个大类:作用于Page、作用于Space以及提供额外信息的Logic类型。redolog记录的是作用于页的,如果作用于Space,那么页号的值为0。

不管是在内存还是磁盘中,redolog都以块为单位进行存储,默认每个块占512B,等于磁盘扇区的大小,这称为redolog block。每个redolog block由3部分组成:日志块头(12B)、日志块尾(4B)和日志主体(492B),log buffer则是由若干个连续的redolog block组成的,总数不能超过1GB个(基于LSN的长度限制)。

InnoDB为了提高redolog的性能和保证数据一致性,还引入的mini-transaction机制(简称mtr),mtr就是redolog组的概念,比如对一些页面的访问、向聚簇索引或二级索引插入一条记录等操作时产生的redolog是不可分割的(插入数据如果引起索引分裂,会产生许多redolog)。每组的最后一条redolog后边会加上一条类型为MLOG_MULTI_REC_END的redolog,来标识该组的结束。

log buffer中写入redolog的过程是顺序的,但不是一条一条写入,而是一个mtr完成后,将里面所有的redolog一起复制到log buffer中(还会把执行过程中可能修改过的页面加入到Buffer Pool的flush链表),也就是存储到redolog block中,可能占用不到一个block,也可能占用多个block。一个事务可以包含多个mtr,那么多个事务的mtr就会有交集,事务间的mtr会相互穿插。

binlog

binlog是属于MySQL Server层面的,又称为归档日志,属于逻辑日志,是以二进制的形式记录的,是sql语句的原始逻辑,主要是用于进行集群中保证主从一致以及执行异常操作后恢复数据。

binlog日志文件默认大小由磁盘决定,顺序追加写入。binlog内存数据存储在binlog cache中(大小由binlog_cache_size控制),磁盘数据则存储在binlog file中。

binlog有三种格式,分别是Row、Statement、Mixed。

  • Row格式记录了操作语句对具体行的操作以及操作前的整行信息,缺点是占空间大(一条sql影响的行数),优点是能保证数据安全,不会发生遗漏,是5.7版本默认格式。
  • Statement格式记录了修改的sql(只是一条sql语句),缺点是在集群中可能会导致操作不一致从而使得数据不一致,如执行now()函数可能会导致不同机器值不同。
  • Mixed格式会针对于操作的sql选择使用Row还是Statement,相比于Row更省空间,但还是可能发生主从不一致的情况。

binlog和redolog虽然都保存了记录的修改日志,但两者有一些区别:

  • binlog是逻辑日志,记录的是对哪一个表的哪一行做了什么修改;redolog是物理日志,记录的是对哪个数据页中的哪个记录做了什么修改。
  • binlog是追加写;redolog是循环写,日志文件有固定大小,会覆盖之前的数据。
  • binlog是Server层的日志;redolog是InnoDB的日志。如果不使用InnoDB引擎,就没有redolog。
© 版权声明
THE END