天天看点

InnoDB体系结构之存储篇

Innodb存储引擎在存储设计上模仿了Oracle的存储结构,其数据是按照表空间进行管理的。所有的数据都要存储在表空间中。

Innodb的表空间分为:

  • 系统表空间

新建一个数据库时,Innodb会初始化一个名为ibdata1的系统表空间。可通过innodb_data_file_path来定义其路径、大小以及自动

扩展策略。例如 innodb_data_file_path= ibdata1:1G:autoextend。

系统表空间中主要包括:数据字典、double write buffer、change buffer、回滚段(undo页的索引信息)、undo表空间(5.6之后

可单独定义其路径)、用户数据(如果innodb_file_per_table=0)等。

  • 独立表空间

可通过innodb_file_per_table=1来让每张表有独立的表空间,每张表有各自的xx.ibd文件,但是表定义信息还是在ibdata1中。这

里面主要存储数据信息(聚集索引)和普通索引。

  • Undo表空间

5.6开始支持创建独立的undo表空间

  • 临时表空间

5.7引入的独立表空间文件ibtmp1。默认值12M。由innodb_tmp_data_file_path定义。通用临时表create temeprry table和SQL执

行过程中产生的内部临时表(using temperory table)会公用这个临时表空间文件。实例启动时创建,关闭时会被删除。5.7中

internal_tmp_disk_storage_engine=Innodb,8.0中internal_tmp_mem_storage_engine=Temptable

  • 通用表空间

5.7引入general tablespace,给8.0做铺垫。但是到了8.0,废弃了MyIsam存储引擎之后之后,一些系统表就放到通用表空间里。

从上面我们已经知道,Innodb的数据存储脱离不了表空间的管理。那么我们从单个表空间文件入手,前面已经知道单个表空间就

是由单个或者多个B+树索引来构成,下面看一下表空间具体的构成:

段(Segment)

既然表空间由B+树构成,那么创建一颗B+树就需要叶子段和内节点段来组成。段是一个逻辑概念,来用来管理物理文件,是构

成索引、表、回滚段的基本元素

簇(Extent)

上面已经知道段是一个逻辑概念,那么物理上簇就是构成段的基本元素。簇由物理上一段连续分配的空间构成。一个段由1个或

者若干个簇组成。默认1个簇由连续的64个page组成,因为一个page的默认值大小为16K,所以一个簇的默认值就是1M.但是这

个大小我们可以设置为1M/2M/4M,由于page也可以更改,所以要通过这两个来决定有多少个page来组成一个簇。每个簇之间的

物理顺序是不连续的。

页(Page)

Innodb最小的I/O单位。默认值16K。5.6可以调整为4k、8k。5.7可以调整为16K、32K。页包括:数据页、undo页、系统页、事

务数据页、插入缓冲页等等。

具体的可以参考下图:

InnoDB体系结构之存储篇

(摘自知书堂优化班)

既然page是最小的I/O单位。那么下面看一下page内部的存储结构以及如何管理。参考下图:

InnoDB体系结构之存储篇

(摘自MySQL运维内参)

文件头信息:(摘自MySQL运维内参)

FIL_PAGE_SPACE_OR_CHECKSUM:4字节。checksum的值

FIL_PAGE_OFFSET:4字节。表示此页面是当前表空间得页面号

FIL_PAGE_PREV:4字节。上个页面

FIL_PAGE_NEXT:4字节。下个页面

FIL_PAGE_LSN:8字节。当前页面最后一次被修改时对应日志的LSN值

FIL_PAGE_TYPE:2字节。

FIL_PAGE_FILE_FLUSH_LSN:8字节。当前Innodb最大被FLUSH到的LSN值

FIL_PAGE_ARCH_LOG_NO_OR_SPQCE_ID:4字节。当前页面属于哪个表空间

页面头信息:(摘自MySQL运维内参)

PAGE_N_DIR_SLOTS:2字节。存储slot个数

PAGE_HEAP_TOP:2字节。当前还没有被使用的空间的最小值

PAGE_N_HEAP:2字节。当前页面的记录数。包括虚拟的最小值最大值

PAGE_FREE:2字节。已经被删除的记录所占用的空间组成的链表首地址

PAGE_GABAGE:2字节。已经被标记为删除的记录数,还没被purge的。如果被purge就放到page_free里

PAGE_LAST_INSERT:2字节。最后被插入的位置

PAGE_DIRECTION:2字节。和上面的insert有关系,表示插入的方向

PAGE_N_DIRECTION:2字节。同一个方向连续插入记录的次数

PAGE_N_RECS:2字节,当前页面存储了多少条记录

PAGE_MAX_TRX_ID:8字节。修改当前页面的所有事务中的最大事务号

PAGE_LEVEL:2字节。当前节点处于B+树第几层

PAGE_INDEX_ID:8字节。当前页面属于哪个索引,存储的索引ID值

PAGE_BTR_SEG_LEAF:10字节。页字段的段头地址

PAGE_BTR_SEG_TOP:10字节。内节点段的段头地址

总之,文件头信息和页面头信息都是为了页面的安全和高效管理来设置。头信息后面就是真正的物理记录,Innodb存储引擎中,

最开始的两条物理记录永远都是虚拟的“最小记录”和“ 最大记录”。这两条记录来限定一个页面的边界。并且,在遍历数据的时

候,搜索到这两条记录说明到了页面的边界,表示本页面的遍历已经结束。

在接下来才算到了真正的行(row)记录。Innodb页面行的管理是通过slot来实现的。就如上图所示,一个slot对应一串行记录,

我们在第一次插入数据页的时候也许物理上行记录是有序的,但是通过后面的增删改操作后,物理上这些行记录其实是已经没有

顺序,但是我们通过修改slot的指针信息,页面以及行的头信息,来让行数据继续有效的管理起来。从上图也可以看出,槽本身

是排序的,在页面的最后位置,槽的长度与页面内存储的记录数有关系,一个槽占2个字节。最高位的槽代表索引最小的记录,

最低位的槽代表索引顺序最大的记录。

图中还可以看到页面尾部还存储了8个字节,前四个字节存的是checksum值,后四个字节存的是头信息中的LSN的值。主要作用

是保证页面的完整性。至此,一个页面里的存储内容以及管理就是这样管理的。

最后,看一下四中行格式:

Redundant:最早的行格式。

Compact:将长字段超过768字节的部分off-page存储。

Dynmic:将长字段完全的off-page存储,只存储20字节的指针。5.7默认的行格式

Compressed:数据页压缩存储,但buffer pool中的数据不能被压缩。

继续阅读