数据库内核月报

数据库内核月报 - 2019 / 08

MySQL · 引擎特性 · 8.0 Innodb redo log record 源码分析

Author: 攒叶

Introduction

redo log对于innodb高效实现事务有至关重要的作用,关于redo log的介绍目前已有许多资料,但大都针对MySQL 5.6、MySQL 5.7版本,内容大都聚集在redo log与事务、redo log与恢复、checkpoint技术等特性上,对于redo log record本身却很少有(甚至几乎没有)资料介绍。目前8.0.16版本,redo log record一共有64种类型,对每种类型都进行详细分析是困难的。本文针对insert语句的redo log类型进行分析,重点分析B+树分裂产生新页时,redo log作为物理日志是如何准确地描述该过程。由于涉及到B+树,本文也会对innodb的数据页进行简单总结。

Index Page

关于索引页,可以查看这篇文章,此处总结索引页的一些关键设计

FIL Header / Trailer

Infimum and Supremum Records

数据页上逻辑最小和最大的记录,数据页被创建时创建,不能被删除,目的是方便页内操作。

Compact Format

变长字段长度列表 NULL字段标记 记录头 字段1数据 字段2数据
0~n 0~m 5 bytes 字段1长度 字段2长度

上表是一个典型的compact格式的行记录,关于compact记录已有许多资料,具体可参考引用。 以下将补充许多资料中讲得相对模糊的地方:

insert && redo log

Flow Chart

insert redo record

上表是一条insert语句对应的redo log记录。值得注意的是,这条insert语句没有涉及到instant列,也不是临时表上的插入,并且上一条记录与本记录的extra_lendata_len都相同(见compact format, 同一张表的不同记录,这两部分的长度可能不同)。

即使是同一种类型的redo record,其解析格式也可能不同,如果上一条记录与本记录的extra_lendata_len都不同,则会额外记录本条记录在page中的偏移,以及本条记录发生mismatch的位置。

关于redo log record的资料很少,本次分析是最为简单的一种,下面简单阐述该表中较为难以理解的部分:

Notes

create page && redo log

可以看到,当插入一条记录时,edo log记录了对对应数据页的修改过程。但当当前page不足以放下新插入的记录时,且邻居page也没有空间时,会触发B+树的分裂操作。具体过程:

// 持有page的X-latch
btr_cur_optimistic_insert--> 
// 当前page空间不足,乐观插入失败, 
// 进行悲观插入之前,btr_pcur_open对父亲子树进行上锁
btr_cur_pessimistic_insert--> 
// 分裂,并将记录插入指定page
btr_page_split_and_insert-->  
...

create page redo log record

btr_page_split_and_insert函数中,调用btr_page_alloc分配新的page,然后调用btr_page_create创建新页,期间调用page_create_write_log生成一条类型为MLOG_COMP_PAGE_CREATEredo log记录,其典型format如下:

type space_id page_no
MLOG_COMP_PAGE_CREATE, 1 byte 1~5 bytes 1~5 bytes

接着在btr_page_set_level方法中,生成一条类型为MLOG_2BYTESrecord,记录pageB+树上的level(叶子节点的level0,根节点level为深度),其format如下所示:

type space_id page_no page_offset val
MLOG_2BYTES, 1 byte 1~5 bytes 1~5 bytes 2 bytes compressed, 1~3 bytes

接下来btr_page_set_index_id方法中,生成一条类型为MLOG_8BYTES,该record表示将对应的page(space_id + page_no )中的PAGE_INDEX_ID字段设置为val值,其format与上述MLOG_2BYTES类似

接下来btr_insert_on_non_leaf_level_func会调用btr_cur_optimistic_insert,或者btr_cur_pessimistic_insert来将聚集索引的非叶子节点的记录插入。聚集索引的非叶子节点的记录形如下:

可以看到非叶子节点的记录与用户表记录实际并没有本质区别,该过程可以看成是往page中插入一条“用户记录”。

由于B+树的分裂是个递归过程,btr_insert_on_non_leaf_level_func函数也会被递归调用,直到调整好B+树。期间会继续不断产生相应的redo record,包括但不限于类型为MLOG_COMP_REC_INSERTMLOG_COMP_PAGE_CREATEMLOG_8BYTES等的redo log record

Reference