innodb是怎样逐步插入一条数据的(三)

开课吧开课吧锤锤2021-03-05 10:18

    Java编程语言是一种简单、面向对象、分布式、解释型、健壮安全、与系统无关、可移植、高性能、多线程和动态的语言。如今Java已经广泛应用于各个领域的编程开发。

Java

    页/Page

    正如上面所说,页就像现实中一本书的书页一样,是innodb中io操作的最小单位。innodb中的页类似于现实中书本的页。

    页的大小默认是16KB;可以通过innodb_page_size参数指定,可选项为:4KB、8KB、16KB、32KB、64KB;

    当pagesize为4、8、16KB时,对应一个extent的page数量同步变化,以保证extent(区/簇)大小保持1M不变。当pagesize为32KB或64KB时,extent内的page数量保证不变,extent同步变为2M和4M;

Java

    每个页都有一个对应的从0开始的编号,这个编号叫做页号。因为表空间的数据文件会被划分成大小相等的页,所以知道页号,再根据文件的初始位置,就可以计算出页在磁盘中的准确地址。

    同理,一张表对应一个聚集索引,而聚集索引元数据中指定了rootpage的页号,因此Innodb引擎可以根据页号和页大小计算出索引B+树rootpage的准确地址,从而对整个表数据进行操作。

    page主要用来存储业务相关的数据,但是为了管理内存分配而存在的extent和segment信息也需要page存储。innodb根据page存储内容不同分以下几类:

    FSPHDR页: 一个表空间可能对应多个数据文件,每个文件都有自己的编号。表空间是数据库中最顶层的结构,通过系统表空间中的元数据可以查询对应的表空间文件等元信息,却无法查询当前表空间对应的段、区等信息,因此也无法获取表空间中页的存储状态。

    为了使表空间的物理存储有一个对外访问的入口,规定表空间中的0号文件的0号page页中存储表空间信息以及当前表空间所拥有的段链表的指针。

    任何一个页都由页头、页身和页尾组成。

    一个page默认16KB,而段和区对应的指针数据量并不大,因此只需要部分头信息就可以维护。而剩下的大部分空间,则用来存储当前表空间拥有的部分发区实体信息。

Java

    页头:指明当前页号、类型和所属表空间。

    页尾:主要用于数据的校验。

    页身:这是页中用来存储数据的主要部分。

    页身又分为表空间首页头信息区和业务数据区。

    FSPHEADER:

    (1):表空间信息:对应空间id、表空间总页数等

    (2):段信息:已写满数据的段实体所在页的链表指针、未写满数据的段实体所在页的链表指针(指向的不是段实体而是段实体所在的页,一页存储85个段实体)。

    (3):碎片区/簇信息:空闲的碎片区/簇(XDES实体本身,不是XEDS实体所在的页)链表指针、未写满的碎片区链表指针、已写满的碎片区链表。这些区/簇信息不属于任何段,而属于表空间,用于给段下次申请空间时分配。

    理论上一个区/簇会完整的分配给一个段,但一些区/簇创建后直接归属表空间,用做碎片区。为了减少浪费,只会把这些区中的部分页分配给一个指定的段。

    例如:当你豪言万丈的宣布要写一部旷世巨著,并要求秘书给你五百页纸时,秘书很可能已经看透了一切,一面是是是的回应你,一面只会给你取3页纸,因为他认为你很可能7天憋不出6个字。同理,innodb给某一个新创建的段分配空间时,并不是一开始就分配一个区/簇,而是从碎片区中先分配32页,只有这32页使用完,innodb才认为这个段是一个大数据段,从而正式开始为其分配一个完整的区/簇。

    数据部分:

    FSPHEADER中指向了段链表和碎片区链表,但这些只是链表指针,真正的区信息节点则存放在当前页的数据区。一个区/簇信息实体称为一个XDESEntry(eXtentDEScript);一页存储256个XDESEntry。

    XDESEntry如上面图示,包含了段id(如果分配给一个段)、碎片区链表中的下一个节点指针等。它不包含页信息,因为区/簇有对应的物理空间,它空间内的页就是拥有的页,因此无需在entry中指明。

    细心的朋友会发现,XDESEntry虽然是描述区/簇,但却没有指定区/簇的编号或地址,那么它到底对应物理空间中哪块区/簇呢?

    区/簇本身没有编号,但区/簇像页一样,也是从文件第一个字节开始连续分配的。同时,每隔256个区/簇的第一个区的第一页就是这256个区/簇的索引页,即XDESpage。

    而XDESpage有pageNo,因此就可以计算出此XDESpage的地址,也即此page所有的区/簇的地址。紧接着的255个区/簇都有一个对应的XDESEntry存储在XDESpage中,这些XDESEntry在此page中位置的偏移量,即为后面255个区/簇的偏移量,从当前XDESpage所有区/簇位置以及对应的偏移量就可以计算出一个XDESEntry对应的区/簇的物理位置。

    FSPHDR页就像一个表空间的封面页,是整个表空间的入口页。

    XDES页:XDES页即eXtentDEScript区/簇描述页的缩写,用来存储区/簇信息实体的页,即存储XDESEntry的页。它除了与FSP页中FSPHEADER不同外,其它内容一模一样。本质上首页也是一个XDES页,只是首页是整个表空间的第一页,因此它又兼职记录了表空间信息。

    XDESEntry:存储了区自身信息的逻辑块。

    因为一页XDES只能存储256个entry,对应256个区,因此逻辑上每隔256个区,就需要一个xdex页来存储下一系列256个区的信息。

    INODE页:同区/簇对应的Entry信息一样,表空间只是指向了各种状态的段页(非段实体)链表,而未存储段信息本身。inode页就是用来存储描述段信息inodeentry的页。

Java

    一个inode页默认存储85条段实体,每个实体又指向了本段对应的不同状态的区/簇链表:未使用的区/簇链表、已写满的区/簇链表、未写满的区/簇链表。

    Index页以上的页均是存储物理空间使用状态,并用于管理区/簇和段本身的页。index页则是用于最终存储业务数据。innodb中表数据是通过聚集索引组织存储的,而叶子节点存储在一个段中,非叶子节点存储在另一个段中,但最终都会存储在Index类型的页中。

    index页详细项如下图:

Java

    index页页内存储结构如下图:

Java

    页内的业务数据是一个逻辑上按顺序排列的单向链表。页内有两条虚拟行,会别代表整个页中索引值最小的行和最大的行,即链表中第一行和最后一行,用来界定链表的范围。

    另外,对于索引段,一页大概有16250B用来存储用户数据。一行包含一个4字节的int类型key,一个指向叶子节点占6字节的页号,大概6字节的rowheader,总共大概16字节。那么一页粗略的计算可以存储16250/16约为1000条。为了优化查询,每隔4-8行数据把这几行数据的第一行地址在存放在一个称为slot的2字节空间中,这些slot一起组成一个称为Pagedirectory的数组中。

    如图:数组最后一个slot存储第一行infimum,倒数据第二个slot存储row4,正序第一个slot存储最后一行数据supremum。这样pagedirectory数组就是一个有序的数组,可以通过一次二分查找算法快速定位数据块,然后在这个块中遍历找到最终符合要求的数据。

    注意:由于用户行与页尾之间有空闲空间,而slot个数受页内行数影响而不固定,即pagedirctory数组长度不固定,因此通过逆序向前追加的方式分配slot。

    以上内容由开课吧老师敖丙提供,更多Java教程尽在开课吧广场Java教程频道。

有用
分享