MySQL InnoDB Engine--数据页存储

测试环境:

MySQL 5.7.28 社区版
CentOS release 6.10 

MySQL Undo参数配置:
innodb_undo_tablespaces    = 1
innodb_default_row_format = dynamic

 

测试脚本:

## 创建测试表
CREATE TABLE `TB001` (
  `ID` varchar(20) NOT NULL,
  `C1` varchar(20) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `IDX_C1` (`C1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

## 第一次插入数据
INSERT INTO `TB001`(ID,C1)VALUES('AA0001','EE0001'),('AA0002','EE0002'),('AA0003','EE0003');

## 第二次插入数据
INSERT INTO `TB001`(ID,C1)VALUES('AA0004','EE0004');

 

数据页存储信息:

 

数据页信息:

## File Header(38字节)
18 B1 2C 2B 00 00 00 03 FF FF FF FF FF FF FF FF 
00 00 00 00 00 26 F4 AA 45 BF 00 00 00 00 00 00 
00 00 00 00 00 18 

## File Header(56字节)
00 02 00 F8 80 06 00 00 00 00 00 DF 00 02 00 03 
00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 29 00 00 00 18 00 00 00 02 00 F2 00 00 
00 18 00 00 00 02 00 32 01 00 

## Infimun Record
01 00 02 00 1C 69 6E 66 69 6D 75 6D 
## spermum Record`
05 00 0B 00 00 73 75 70 72 65 6D 75 6D

## AA001
06 06 00 00 10 00 20 41 41 30 30 30 31 00 00 00 00 05 07 A7 00 00 00 69 01 10 45 45 30 30 30 31 
## AA002
06 06 00 00 18 00 20 41 41 30 30 30 32 00 00 00 00 05 07 A7 00 00 00 69 01 1E 45 45 30 30 30 32 
## AA003
06 06 00 00 20 00 20 41 41 30 30 30 33 00 00 00 00 05 07 A7 00 00 00 69 01 2C 45 45 30 30 30 33 
## AA004
06 06 00 00 28 FF 91 41 41 30 30 30 34 00 00 00 00 05 0C AA 00 00 00 6C 01 10 45 45 30 30 30 34

## Page Directory
70 00 63 

## File Trailer(8字节)
18 B1 2C 2B 00 26 F4 AA

使用图表示:

PS: 由于Page Header和Filer Header太多,上图未画全所有列信息。

 

File Header使用38字节来存放文件头信息

Page Header使用56字节来存放数据头信息

 

Infimum record和Supermum record:

虚拟的行记录,限定记录的边界。
infimum记录的是比页中任何主键都要小的值。
supermum值比任何可能大的值还要大的值。
在页创建时被创建,任何情况下不会被删除,在compact和redundant行格式下,占用的字节数不同。

 

File Trailer(8字节):

使用8字节来检测数据页是否损坏:
1、前4字节来存放checksum值,与File Header中的FIL_PAGE_OR_CHECKSUM相对
2、后4字节存放FILE_PAGE_LSN,与File Header中的FIL_PAGE_LSN相对

在读取数据页时,会对比File Trailer和File Header中的值,判断该数据页是否存在损坏。

在MySQL 5.6版本中,可以通过参数innodb_checksum_algorithm来控制检测checksum函数的算法。

默认为crc32,可选值为:innodb、crc32、none(不启用)、strict_innodb、strict_crc32、strict_none

 

Page Directory(页目录):

1、由于B+树原理,每个数据页至少存放两条或N条记录,通过Page Directory中存放的指针或槽(slot)来标识这些记录在数据页中的位置。
2、对于InnoDB存储引擎,使用稀疏目录来存放指针或槽(slot)信息,单个指针或槽(slot)可能对于多条记录。

 

User Records和Free Records:

1、User Records用来存放用户数据的空间。
2、Free Records指未使用的空间。

 

InnoDB存储引擎数据行格式:

InnoDB存储引擎共有四种数据行格式:
DYNAMIC:MySQL 5.7默认格式
COMPACT:MySQL 5.6版本中默认格式
REDUNDANT:为兼容MySQL早期版本
Compressed:在MySQL官网已查找不到该格式。
PS:在MySQL 5.7.9版本引入参数innodb_default_row_format来控制数据行格式。


COMPACT与REDUNDANT的主要区别在于如何记录标识记录行中的动态列位置:
COMPACT行格式:非null变长字段列表,按照列的顺序逆序放置,列的长度小于255,用1字节表达;大于255,则用2字节
REDUNDANT行格式:字段长度偏移列表,按照列的顺序逆序放置,列的长度小于255,用1字节表达;大于255,则用2字节


COMPACT与COMPACT的主要区别在于如何存放溢出列数据:
COMPACT行格式:使用20字节指针来存放溢出列数据位置。
DYNAMIC行格式:使用20字节指针来存放溢出列数据位置,并在当前数据页中存放溢出列前758个字节。


NULL值存放:
REDUNDANT行格式:对于定长列的NULL值会占用数据空间,对于变长列不会占用数据空间。
COMPACT/DYNAMIC行格式:对于定长列和边长列的NULL值都不占用数据空间。

 

对于常用的COMPACT/DYNAMIC行格式,每条记录中会包含如下记录行信息:

1、每个允许为NULL的列需要额外的1byte空间来标识其是否为NULL
2、每个变长列需要额外的1字节或2字节来标识该列的存储长度。
3、每条记录使用5字节存放记录头信息(记录类型/记录列数/下一条记录地址)
4、每条记录使用6个字节存放事务信息
5、每条记录使用7个字节存放回滚指针
6、每条记录使用6字节存放RowID(非聚集索引记录)

行记录使用5字节40位来存储如下头信息:
预留位1(1bit):没有进行使用
预留位2(1bit):没有进行使用
delete_mask(1bit):是否被删除
min_rec_mask(1bit):该记录是否为B+树中非叶子节点的最小记录
n_owned(4bit): 当前嘈管理的记录数
heap_no(13bit):当前数据在记录堆的位置
record type(3bit):0表示普通记录,1表示B+树非节点记录,2表示最小记录,3表示最大记录
next_record(16bit):下一条记录的相对位置

对于一张仅包含INT主键的表,其每行记录也需要:
4字节INT主键+5字节记录头信息+6字节事务信息+7字节回滚指针信息+页目录(Page Directory)槽信息=24字节
而单个数据页16*1024=16384字节 - (File Header使用的38字节 + Page Header使用的56字节 + File Trailer使用的8字节=16282字节。
因此每个数据页可以存放16282/24=678条记录,再扣除Infimum record和Supermum record两条,实际能存放676条记录。


对于一张仅包含INT主键的表,其每行记录也需要:
8字节INT主键+5字节记录头信息+6字节事务信息+7字节回滚指针信息+页目录(Page Directory)槽信息=28字节
而单个数据页16*1024=16384字节 - (File Header使用的38字节 + Page Header使用的56字节 + File Trailer使用的8字节=16282字节。
因此每个数据页可以存放16282/28=581条记录,再扣除Infimum record和Supermum record两条,实际能存放579条记录。
posted @ 2020-01-10 23:40  TeyGao  阅读(343)  评论(0编辑  收藏  举报