MYSQL数据库、缓存

InnoDB数据页结构分析

2019-11-01  本文已影响0人  漫步无法人生

自己分析一下ibd文件还是蛮有意思的,能够学到不少东西,建议跟着走一遍,慢慢领会作者设计的意图
人学东西总是先感性的认识,慢慢到理性 —— 过程中大脑需要理解和消化

mysql版本5.7.26

先贴一张数据页的结构图-方便对整体有个印象

InnoDB页结构示意图 InnoDB页各组成部分简单描述

利用工具查看数据库文件ibd的页分布情况 (工具名py_innodb_page_info)

当你熟悉page的格式后,自己也能写一个这样的工具


test.ibd文件页分布情况

使用十六进制工具Synalyze It! 打开/usr/local/mysql/data/nishui/test.ibd 文件(文件权限问题此处忽略,二进制工具任意)

找到 00000006 页在文件中的位置

因为看的是00000006数据页, 用6 * 16 * 1024 = 98304B (innodb引擎默认一页16KB(可通过innodb_page_size改变页大小),然而1KB= 1024B) 转换成十六进制为 0x18000
show global status like 'Innodb_page_size' 可查看当前页大小

找到该位置截图如下:


06页开始逻辑位置

这个页便是 00000006 数据页开始的位置了, 可以开始分析详细数据了

一、先分析File Header(38字节-描述页信息)

二、分析Page Header(56字节-记录页的状态信息)

SHOW TABLE STATUS LIKE 'test'查看表行记录格式

小结一下
1.innodb在整个页可以使用的空间当成heap,当需要插入记录的时候,首先会检查PAGE_FREE指向的空闲空间,若申请的空间小于等于该空间容量时,那么使用该空闲空间,否者从PAGE_HEAP_TOP指向的空闲空间进行分配
heap中存储的记录非物理连续的,只是逻辑上连续的,可用下图表示
2.PAGE_LAST_INSERT、PAGE_DIRECTION、PAGE_N_DIRECTION主要使用来做页分裂操作的


数据页中行记录存储的排列方式

三、伪记录分析Infimum + Supremum Record (26字节-两个虚拟行记录)

innodb存储引擎有两个伪记录,用来界定行记录的边界
数据从 0x1805E 到 0x18077


分析下伪记录中的recorder header中的next_record

recorder header最后两个字节 0x001E,表示下一个记录位置的偏移量,即当前行记录“内容”的位置0x18063 + 0x001E,即0x18081,这个位置就是存放第一条实际用户记录


四、分析User Record

当前 Row Format 为Compact格式 可通过命令show table status like 'table_name' 进行查看

CREATE TABLE `test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `t1` varchar(10) DEFAULT NULL,
  `t2` varchar(15) DEFAULT NULL,
  `t3` int(11) DEFAULT NULL,
  `t4` varchar(1500) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

创建数据的脚本
CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_test`( )
BEGIN
  #Routine body goes here...

declare i int;
declare tmp int;
set i=1;
set tmp = 1;
while i<200 do
    if tmp=1 then
        insert into test (t1, t4) values('a', REPEAT('a', i)); 
        set tmp = 0;
    else
        insert into test (t1, t3, t4) values('a', i, REPEAT('a', i)); 
        set tmp = 1;
    end if;
    set i=i+1;
end while;
END

由上面伪记录Infimum的Record Header可知下一条记录的开始地址是0x18081,顺便把前面的extra info也分析下,截图如下


分析User Record中Record Header中的内容

0x 00 00 10 00 CC 转换成十进制如下 00000000 00000000 00010000 00000000 11001100
下面都是二进制的,其他的都是十六进制的

简单用图可表示如下(忽略实际内容):


五、分析Page Directory

这一页的末尾是0x1BFFF,并且加上Page Header中PAGE_N_DIR_SLOTS,能够知道Page Directory中包含了7个slot 截图如下


位置是从 0x1BFEA - 0x1BFF7,一共14个字节,因此展开如下:

六、分析File Tailer

固定占用8个字节,并且是在页尾部,可以直接得出位置为0x1BFF8 开始的


为了保证页能够完整地写入磁盘(如可能发生的写入过程中磁盘损坏、机器宕机等原因),InnoDB存储引擎的页中设置了File Trailer部分。File Trailer只有一个FIL_PAGE_END_LSN部分,占用8个字节。前4个字节代表该页的checksum值,最后4个字节和File Header中的FIL_PAGE_LSN相同。通过这两个值来和File Header中的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN值进行比较,看是否一致(checksum的比较需要通过InnoDB的checksum函数来进行比较,不是简单的等值比较),以此来保证页的完整性(not corrupted)。

数据页格式

File Header

名称 大小(字节) 说明
FIL_PAGE_SPACE_OR_CHKSUM 4 当mysql为4.0.14之前的版本时,该值为0。在之后的mysql版本中,该值代表页的checksum值(一种新的checksum值)
Fil_PAGE_OFFSET 4 表空间中页的偏移值,如果独立表空间a.ibd的大小为1GB,如果页的大小为16kb,那么总共有65536个页.FIL_PAGE_OFFSET表示该页在所有页中的位置。若此表空间的ID为10,那么搜索页(10, 1)就表示在表a中的第二页
FIL_PAGE_PREV 4 当前页的上一页,B+树的特性就决定了叶子节点必须是双向列表
FIL_PAGE_NEXT 4 当前页的下一页
FIL_PAGE_LSN 8 该值代表页最后被修改的日志序列位置LSN
FIL_PAGE_TYPE 2 INNODB 存储页的类型,
FIL_PAGE_FILE_FLUSH_LSN 8 该值仅在系统表空间的一个页中定义,代表文件至少更新到了LSN值。对于独立表空间,该值为0
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 从mysql4.1开始,该值代表属于哪个表空间

Innodb存储引用中页的类型

名称 十六进制 解释
FIL_PAGE_INDEX 0x45BF B+树叶节点
FIL_PAGE_UNDO_LOG 0x0002 undo log页
FIL_PAGE_INODE 0x0003 索引节点
FIL_PAGE_IBUF_FREE_LIST 0x0004 insert buffer空闲列表
FIL_PAGE_TYPE_ALLOCATED 0x0000 该页为最新分配页
FIL_PAGE_IBUF_BITMAP 0x0005 insert buffer 位图
FIL_PAGE_TYPE_SYS 0x0006 系统页
FIL_PAGE_TYPE_TRX_SYS 0x0007 事务系统数据
FIL_PAGE_TYPE_FSP_HDR 0x0008 File space Header
FIL_PAGE_TYPE_XDES 0x0009 扩展描述页
FIL_PAGE_TYPE_BLOB 0x000A BLOB页

Page Header

用来数据页的状态信息,14部分组成,共56字节

名称 大小(字节) 说明
PAGE_N_DIR_SLOTS 2 在Page Directory (页目录〉中 的Slot (槽〉 数,“4.4.S Page Directory” 小节中会介绍
PAGE HEAP TOP 2 堆中第一个记录的指针, 记录在页中是根据堆 的形式存放的 堆中空闲空间的位置(偏移量)
PAGE N HEAP 2 堆中的记录数. 一共占用2 字节, 但是第15 位表示行记录格式 (包括最小和最大记录以及标记为删除的记录)
PAGE FREE 2 指向可重用空间的首指针 指向页中空闲空间的位置(偏移量)(就是标记为删除的记录地址)
PAGE GARBAGE 2 己删除记录的字节 数, 即行记录结构中也阳也在为1的记录大小的总数
PAGE LAST INSERT 2 最后捕入记录的位置(偏移量)
PAGE DIRECTION 2 最后插入的方向. 可能的取值为2 。 1.PAGE LEFT (0x01) 2.PAGE RIGHT (Ox02) 3.PAGE SAME REC (Ox03) 4. PAGE SAME PAGE (Ox04)
PAGE N DIRECTION 2 一个方向连续插入记录的数量
PAGE N RECS 2 该页中记录的数量
AGE MAX TRX ID 8 修改当前页 的最大事务ID, 注意该值仅在Secondary Index中定义
PAGE LEVEL 2 当前页 在索引树中的位置, OxOO代表叶节点, l!P时节 J点总是在第0层
PAGE INDEX ID 8 索引ID, 表示当前页属于哪个索引
PAGE BTR SEG LEAF 10 B+树数据页非页节点所在段的segment header。 注意该值仅在 B+ 树的 Root 页中定义
PAGE BTR SEG TOP 10 B+树数据页所在段的 segment header. 注意该值仅在 B+树的 Root 页中定义

COMPACT行记录格式

COMPACT行记录格式
名称 大小(bit) 描述
预留位1 1 没有使用
预留位2 1 没有使用
delete_mask 1 标记该记录是否被删除
min_rec_mask 1 标记该记录是否为B+树的非叶子节点中的最小记录
n_owned 4 表示当前槽管理的记录数
heap_no 13 表示当前记录在记录堆的位置信息
record_type 3 表示当前记录的类型,0表示普通记录,1表示B+树非叶节点记录,2表示最小记录,3表示最大记录
next_record 16 表示下一条记录的相对位置
Total 40(Byte) nothing

参考借鉴:

InnoDB数据页结构
MYSQL内核:INNODB存储引擎 卷1-4
MySQL技术内幕InnoDB存储引擎第2版

上一篇 下一篇

猜你喜欢

热点阅读