InnoDB存储空间
关键词
- 表空间、段、区、页、行
- 一个段256M,一个区1M,一个页16K,一个段=256区=256*64个页
- 表空间中每连续256个区需要一个XDES来维护,XDES里面有256个entry,每一个entry描述一个区的状态、区里面64个页的状态、上一个和下一个区的指针、区属于哪一个段。
- 段分为数据段,索引段,回滚段等,在 MySQL中,数据是按照B+树来存储,因此数据即索引,因此数据段的页即为B+树的叶子节点,索引段的页为B+树的非叶子节点。
- Page链接起来就是一个双向链表的结构
- B+ 树在查找对应的记录时,并不会直接从树中找出对应的行记录,它只能获取记录所在的页,将整个页加载到内存中,再通过 Page Directory 中存储的稀疏索引和 n_owned、next_record 属性取出对应的记录
https://blog.jcole.us/2013/01/04/page-management-in-innodb-space-files/
https://blog.jcole.us/innodb/
索引组织表
层级划分
InnoDB 实现的表空间是在文件系统之上又构建的一层逻辑存储的空间管理
InnoDB 最小读写单位是 Page,page 采用默认的 16K
为了高效管理,又将连续的 64 个页称为 “extent”,还在 extents 上层添加了一个 segment inode 管理,最多可以管理 256 个 extents
一个表空间文件,实际就是一系列 Pages 的组合 (最大 232),为了方便管理,你可以理解为做了三级的划分,分别是 segment、extent、page 。
段:ibd表空间文件会被分成多个段(segment),每个段和一个索引关联
区:段以区(extent)为大小进行增长或缩小,一个区只能属于一个段,并且大小为1MB
页:页(page)是区的子元素,默认大小16KB
行:一个页可以包含2到N行数据,这取决与每个行的大小,但因为限制了最小存放2行数据,所以每一行的大小必须小于8000bytes
1段 = 256区 = 256*64页
查看页信息
查看每个页的详细信息
innodb_page_info -v dept_emp.ibd
也可以使用py_innodb_page_info工具
> python py_innodb_page_info.py/usr/local/mysql/data/ibdata1
Total number of page:83584
Insert Buffer Free List:204
Freshly Allocated Page:5467
Undo Log Page:38675
File Segment inode:4
B-tree Node:39233
File Space Header:1
可以看到共有83 584个页,其中插入缓冲的空闲列表有204个页、5467个可用页、38 675个Undo页、39 233个数据页,等等。你可以通过添加-v参数来查看更详细的内容。
表空间
Space File 主要包括了两类:系统空间 (system space) 和 表空间 (Per-table space files)。表空间需要打开 innodb_file_per_table 变量之后才会生效
Page0 肯定是 “File SPace HeaDeR”,其中维护了 extent 相关的信息,例如那些是空闲的、满的、有碎片的 (fragmented)。在 FSP_HDR 页中,只能维护 256 个 extent 相关的信息 ( 256 extent==16,384 pages==256MiB),这也就导致每隔 256M 都需要有一个 XDES 来管理接下来的 extent 元信息。
- File Segment ID:extent属于哪个段
- List node for XDES list:指向上一个和下一个extent
- State:区状态
- Page State Bitmap:区里面每一个页的状态,一个页的状态用2个位表示,一个区1M,一个页16K,一个区=64个页,64 x 2 = 128 bits = 16bytes
段
表空间是由不同的段组成的,常见的段有:数据段,索引段,回滚段等等。
在 MySQL中,数据是按照B+树来存储,因此数据即索引,因此数据段的页即为B+树的叶子节点,索引段的页为B+树的非叶子节点。
回滚段用于存储undo日志,用于事务失败后数据回滚以及在事务未提交之前通过undo日志获取之前版本的数据,在InnoDB1.1版本之前一个InnoDB,只支持一个回滚段,支持1023个并发修改事务同时进行,在InnoDB1.2版本,将回滚段数量提高到了128个,也就是说可以同时进行128*1023个并发修改事务。
每个段在开始时,会根据数据量的需要,逐步增加页的数量,当页数量超过32页时,会以64个页为单位,增大页数量。
页
innoDB存储引擎中,常见的页类型有:
- 数据页(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)
数据页
页是 InnoDB 存储引擎管理数据的最小磁盘单位,而 B-Tree 节点就是实际存放表中数据的页面。
每一个页中包含了两对 header/trailer:内部的 Page Header/Page Directory 关心的是页的状态信息,而 Fil Header/Fil Trailer 关心的是记录页的头信息。在页的头部和尾部之间就是用户记录和空闲空间了,每一个数据页中都包含 Infimum 和 Supremum 这两个虚拟的记录(可以理解为占位符),Infimum 记录是比该页中任何主键值都要小的值,Supremum 是该页中的最大值: User Records 就是整个页面中真正用于存放行记录的部分,而 Free Space 就是空余空间了,它是一个链表的数据结构,为了保证插入和删除的效率,整个页面并不会按照主键顺序对所有记录进行排序,它会自动从左侧向右寻找空白节点进行插入,行记录在物理存储上并不是按照顺序的,它们之间的顺序是由 next_record 这一指针控制的。
B+ 树在查找对应的记录时,并不会直接从树中找出对应的行记录,它只能获取记录所在的页,将整个页加载到内存中,再通过 Page Directory 中存储的稀疏索引和 n_owned、next_record 属性取出对应的记录,不过因为这一操作是在内存中进行的,所以通常会忽略这部分查找的耗时。 page分为叶子页(leaf page)和非叶子节页(non-leaf page),叶子页包含了实际的行数据,非叶子页只包含了非叶子页指针或叶子页指针。B+树是平衡的,所以所有的分支都有一样的深度。
InnoDB为每一个page分配了一个level级别:叶子页的level是0,随着树分支往上走,level不断递增。
所有的非root页和非叶子页的页面,都被称为internal page。
无论是叶子页还是非叶子页, 里面包含的行(record)都有指向下一行的指针(在同一个page里的偏移量),record链表从infimum开始,按键升序链接所有记录,终止于supremum。需要注意的时,在page里面的record数据,从物理角度来讲,在page里面并不是顺序存储,行在插入到page时,哪里有空缺空间就会插入到哪里,所以它们需要依赖的顺序只有链表上的顺序。 同一个level的page,每一个page会有一个指针(FIL头部)指向上一个和下一个page
行
InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据。
SHOW TABLE STATUS LIKE “table_name”,命令可以查看行格式
Compact
- 变长字段长度列表:此字段标识列字段的长度,与列字段顺序相反存放,若列长度小于255字节,用一个字节表示,若大于255字节,用两个字节表示,这也是 MySQL的VARCHAR类型最大长度限制为65535
- NULL标志位:标识改列是否有空字段,有用1表示,否则为0,该标志位长度为ceil(N/8)(此处是 MySQL技术内幕-InnoDB存储引擎与官方文档有出入的地方);
-
记录头信息:固定用5字节表示,具体含义如下:
- 列数据:此行存储着列字段数据,Null是不占存储空间的;
- 隐藏列:事务id和回滚列id,分别占用6、7字节,若此表没有主键,还会增加6字节的rowid列。
Redundant
- 字段长度偏移列表:存储字段偏移量,与列字段顺序相反存放,若列长度小于255字节,用一个字节表示,若大于255字节,用两个字节表示
-
记录头信息:固定用6字节表示,具体含义如下:
- 隐藏列:事务id和回滚列id,分别占用6、7字节,若此表没有主键,还会增加6字节的rowid列。
行溢出
关于行溢出,即Redundant格式、Compact格式存储很长的字符串,在该字段会存储该字符串的前768个字节的前缀(字段超过768字节则为变长字段),并将整个字符串存储在uncompress blob页中。
索引
InnoDB 存储引擎在绝大多数情况下使用 B+ 树建立索引,B+ 树是平衡树,它查找任意节点所耗费的时间都是完全相同的,比较的次数就是 B+ 树的高度。当数据必须从磁盘读取时,B+树可以保证最多的读取次数(取决于树的高度)。聚集索引和辅助索引
数据库中的 B+ 树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),它们之间的最大区别就是,聚集索引中存放着一条行记录的全部信息,而辅助索引中只包含索引列和一个用于查找对应行记录的『书签』。
聚集索引
聚集索引就是按照表中主键的顺序构建一颗 B+ 树,并在叶节点中存放表中的行记录数据。
当我们使用聚集索引对表中的数据进行检索时,可以直接获得聚集索引所对应的整条行记录数据所在的页,不需要进行第二次操作。
辅助索引
数据库将所有的非聚集索引都划分为辅助索引,但是这个概念对我们理解辅助索引并没有什么帮助;辅助索引也是通过 B+ 树实现的,但是它的叶节点并不包含行记录的全部数据,仅包含索引中的所有键和一个用于查找对应行记录的『书签』,在 InnoDB 中这个书签就是当前记录的主键。