索引组织表
聚集索引, 隐式row_id
表逻辑结构:表空间-段-区-页-行

-
段
-
叶子节点段(数据段)
新建的数据段只有32个页(16K)的碎片页,不足一个区,用完后在分配连续的区
-
非叶子节点段(索引段)
-
undo段
-
-
区,区是连续的页,一个区大小为 1M
-
页,也叫块,innodb 磁盘管理的最小单位。存储页大小为 16K,即一个区有96个页。
-
数据页(B-tree Node)
-
undo页(undo Log Page)
-
系统页(System Page)
-
事务数据页(Transaction system Page)
-
插入缓冲位图页(Insert Buffer Bitmap)
-
插入缓冲空闲列表页(Insert Buffer Free List)
-
未压缩的二进制大对象页(Uncompressed BLOB Page)
-
压缩的二进制大对象页(compressed BLOB Page)
-
-
行,每个页最多允许存放16KB/2-200行的记录,即7992行记录
行结构
compact 格式

变长字段长度
-
每个变长字段长度最大2字节表示,也就是变长字段最大 65536 byte
-
列表以逆序顺序表示。如实例 line1,第一个变长字段长度是1,第二个是 3
null 标志位
-
初始1字节。若有超过8个字段可以为 null,则增加一个字节表示。详见这篇文章
-
只关心可以为 null 的字段,每个 bit 标志一个可为 null 的字段,逆序排列。
-
null 在数据里不占据实际空间,只是有一个 bit 作为标记
记录头
-
delete_flag 表示已经被标记删除(并不是SQL delete的时候立即物理删除,有后台线程定时清理、合并空间)
-
当前记录是否是当前页最小虚拟记录
-
record_type
-
000,表示普通记录
-
001,表示B+树非叶子节点
-
010,infimum
-
011,supremum
-
-
next_record,下一条数据的相对偏移量,占 2 字节。同时意味着当前记录,在当前空间的最大长度是 2^16,也就是 25536 字节
reduntant 格式
略
行溢出数据
* 不定长数据到底如何存储?前面说过,下一条数据偏移量占2字节,也就是说,当前数据长度最多 25535 字节,varchar 最大也是 25535,那么一条记录能存下一条 25535 的 varchar 吗?
如图:

换个编码:

从图中可以看出,varchar 的限制单位是字节,而不是一个字,utf8 通常按3个字节表示,所以图中最大是 21845。实测中发现有的SQL工具可以执行成功以上的创建命令,但是检查表信息,发现把字段 a 标记成了 text 类型。
当行数据大小超过 25535 时,会发生行溢出。行溢出时,数据存放在页类型为Uncompress BLOB页中
-
数据库的每个数据页大小为 16K,而行最大的长度是 65535,也就是 64K,那么行数据会跨页存储吗?
数据库通常是以 ID 为聚集索引,构建 B+ 树,而叶子节点的最小单位是页。也就是说,索引只是找到对应数据页,具体是页中的那一条行记录,是加载数据页到内存,然后遍历得出。
而作为 B+ 树节点,至少应该有两条记录(否则失去了 B+ 树的意义,变成了链表),因此当数据页只能存下一条行记录,InnoDB 会自动把该行溢出。所以,每条行记录实际存储在数据页(B+树叶子节点)中的大小,最多 8K (实际还要小一点,因为还有页的属性的字段要存),溢出的数据,会被放到 Uncompressed BLOB Page。

char 和 varchar
通常认为 char 是定长的,varchar 是变长的。但是有些细节还是要注意
-
char 的 limit 单位是字符,而不是字节。如 char(2) 在 utf8 字符集下可以存 “你好”,实际占用占用 6 字节。不足 2 字符的,会在前面自动填充 0x20
-
varchar 的 limit 单位是字节
-
char 会和 varchar 一样,在变长字段长度列表中,记录字符串真实长度
页结构

-
File header
-
File type, 页的类型,有数据页、索引页、系统页、事务数据页、BLOB页 等等
-
Page_prev 和 Page_next,前一个页和后一个页的指针(B+树的特性决定叶子节点是双向链表)
-
Page offset,int32,页的偏移量,表示当前页的 ID,2^32 * 16KB = 64 TB,64TB 就是 Innodb 理论上最大的存储限制
-
-
Page header,有不少字段,主要是用来管理可用空间的
-
Infimum & supremum,是两个虚拟的记录,行记录在页中是以链表的形式存储,这两个记录可以看作链头和链尾

-
User records, 实际的行记录数据
-
Free space,空闲空间,数据被删除后,该空间会被加入到这个链表
-
page directory,业内记录的稀疏目录,用于加载到内存后的二分查找
-
file trailer,最主要的字段是 checksum,校验和,用于判断页的数据是否完整写入到磁盘
网友评论