一、概述
在这篇博客中,我会介绍
- InnoDB存储引擎表的逻辑存储及实现
- 重点分析表的物理存储特征(如行结构和页结构)。
在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式叫做索引组织结构(index organized table)。在InnoDB存储引擎中,每张表都有个主键,如果在创建表的时候没有指定primary key,会自动选择或者生成:
- 选择第一个非空唯一索引(not null、unique)
- 自动创建一个6字节大小的指针
二、InnoDB逻辑存储结构
所有数据都被逻辑地存放在一个空间中,成为表空间(tablespace)。表空间由段(segment)、区(extent)、页(page)组成,
2.1 表空间
表空间可以看做是逻辑存储结构的最高层,所有数据都放在这里。默认情况下InnoDB有一个共享表空间ibdata1,如果用户设置了innodb_file_per_table这个参数,则每张表的数据可以单独放到一个表空间,但是这个独立的表空间只有数据、索引、插入缓冲Bitmap页,其他的undo信息、插入缓冲索引页、系统事物信息、二次写缓冲等还是放在原来的表空间内。
2.2 段
表空间是由段组成的,常见的段有数据段、索引段、回滚段等。数据段就是B+树的叶子节点,索引段就是B+树的非页节点。
2.3 区
区是由连续页组成的空间,在任何情况下每个区的大小都为1MB,一个区一共有64个连续的页,每个页的大小为16KB,每个页存储7922行记录。
2.4 页
页是InnoDB磁盘管理的最小单位。每个页默认大小为16KB,也可以通过参数innodb_page_size来设置大小为8KB、4KB。
常见的页类型:
- 数据页(B-tree Node)
- undo页(undo Log Page)
- 事物数据页(Transcaction system Page)
- 插入缓冲位图页(Insert Buffer Bitmap)
- 插入缓冲空闲列表页(Insert Buffer Free List)
2.5 行
InnoDB存储引擎是面向行的(row-oriented),也就是说数据是按行进行存放的。每个页16KB,因此每个页最多允许存放7922行记录。比如像MySQL infobraght引擎是按列进行存放数据的。
三、InnoDB行结构
InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据。
3.1 Compact行记录格式
- 每行数据除了用户定义的列外,还有两个隐藏列,事物ID列和回滚指针列,分别为6字节和7字节的大小。若没有定义主键,还会增加一个6字节的rowid列。
- InnoDB存储引擎在页内部通过一种链表的结构来串联各个行记录。
- 在Compact格式下NULL不占用任何存储空间。
3.2 Redundant行记录格式
3.3 行溢出数据
在一般情况下,InnoDB存储引擎的数据都是存放在页类型为B-tree node中。但是当发生行溢出时,数据存放在页类型为Uncompree BLOB页中。
InnoDB存储引擎表是索引组织的,即B+树的结构,每个页中至少得有两条行记录(否则失去了B+Tree的意义,退化成链表了)。因此如果一个页只能放下一条记录,那么InnoDB存储引擎会自动将行数据放到溢出页中。
3.4 Compressed和Dynamic行记录格式
这两种新格式对于存放在BLOB中的数据采用了完全行溢出的方式。
3.5 Char的行结构存储
通常理解VARCHAR是存储变长长度的字符类型,CHAR是存储固定长度的字符类型。
对于多字节字符集编码的CHAR数据类型的存储,Char类型被明确视为了变长字符类型。
四、InnoDB页结构
InnoDB数据页由以下7个部分组成
- File Header
- Page Header
- Infimun和Supremum Records
- User Records(用户记录,行记录)
- Free Space(空闲空间)
- Page Directiory(页目录)
- File Trailer
4.1 File Header
记录页的一些头信息
- 表空间中页的偏移,比如独立表空间a.ibd=1G,如果页为16KB,那么一共有65535个页,FIL_PAGE_OFFSET表示该页在65535个页的第几个,从0开始编号。
- 当前页的上一个页、下一个页
- InnoDB存储引擎页的类型。FIL_PAGE_TYPE = Ox45BF是数据页
- 页属于哪个表空间。
4.2 Page Header
记录数据页的状态信息,由14个部分组成
- PAGE_N_DIR_SLOTS:在Page Directory(页目录)中Slot的数量。
- PAGE_HEAP_TOP:空闲空间开始位置的偏移量,堆中第一个记录的指针,记录在页中以堆的形式存放
- PAGE_N_HEAP:堆中的记录数。
- PAGE_FREE:指向可重用空间的指针
- PAGE_GARBAGE:已删除记录的字节数。
- PAGE_LAST_INSERT:最后删除记录的位置
- PAGE_DIRECTION:最后插入的方向
- PAGE_N_DIRECTION:一个方向连续插入的记录数量
- PAGE_N_RECS:该页中记录的数量
- PAGE_MAX_TRX_ID:修改当前页的最大事物ID(仅在Secondary Index中定义)
- PAGE_LEVEL:当前页在索引树中的位置,0x00代表叶节点,叶节点总是在0层。
- PAGE_INDEX_ID:索引ID,表示当前页属于哪个索引。
- PAGE_BTR_SEG_LEAF:B+树数据页非页节点所在段的segment header。(仅在B+树 Root页定义)
- PAGE_BTR_SEG_TOP:B+树数据页所在段的segment header(仅在B+树的Root页定义)。
4.3 Infimum和Supremum Record
每个数据页由两个虚拟的行记录,用来限定记录的边界。Infimum记录的是比该页所有主键值都小的值,Supremum指的是比所有主键值大的值。
4.4 UserRecord和FreeSpace
UserRecord就是实际存储行记录的地方,B+树索引组织。
FreeSpace是指空闲空间,以链表组织,一条行记录被delete掉,该空间会加入该链表中。
4.5 Page Directory
B+树索引本身并不能找到一条具体的行记录,能找到的只是该记录所在的页,数据库把页载入到内存,再通过Page Directory进行二分搜索。
4.6 File Trailer
检测页是否已经完整的写入磁盘。通过比较该File Trailer中的checksum值与File Header部分的checksum值(通过checksum函数进行比较)来保证页的完整性。