前言
本文是作者学习《掘金小册MySQL到底是怎样运行的》时记录下的部分笔记,若有侵权请联系作者删除
InnoDB和磁盘的交互方式
将数据划分成若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为16kb
InnoDB行格式
innoDB目前有四种行格式
一条完整的记录可以被分为记录的额外信息和记录的真是数据两部分。
记录的额外信息
记录的额外信息是服务器为了描述这条记录而添加的一些信息,记录的额外信息包括三部分,分别是变长字段长度列表、null值列表、记录头信息。
各变长字段数据占用的字节数按照列的顺序逆序存放。变长字段长度列表中只存储值为非null的列内容占用的长度。
Page Directory(页目录)
- 将所有正常的记录(包括最大和最小记录,不包括标记已删除的记录)划分为几个组。
- 每个组的最后一条记录(也就是组内最大的那条记录)的头信息中的n_owend属性表示该记录拥有多少条记录,也就是改组内共有几条记录。
- 将每个组的最后一条记录的地址偏移量单独取出来按照顺序存储到靠近也的尾部的地方,这个地方就是所谓的page directory,也就是页目录。页面目录中的这些地址偏移量被称为槽,所有这个页面目录就是由槽组成
InnoDB分组规则
对于最小记录所在的分组只能有1条记录,最大记录所在的分组拥有记录条数在18条之间,剩下的分组中记录的条数范围只能在48条之间。
- 初始情况下一个数据页里只有最小记录和最大记录两条记录,他们属于两个分组
- 之后每插入一条记录,都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽,然后把该槽对应的记录的n_owned值加1,表示本组内又添加了一条记录,直到该组中记录数等也8个。
- 在一个组中的记录数等于8个后在插入一条记录时,会将组中的记录拆分成两个组,一个组中四条记录,一个组中五条记录。这个过程中会在页目录中新增一个槽来记录这个新增分组中最大的那条记录的偏移量。
在一个数据页中查找指定主键值的记录的的过程分为两步
- 通过二分法确定该记录所在的槽
- 通过记录的next_record属性遍历该槽所在组中的各个记录
过程:
比方说我们查找主键值为x的记录,计算中间槽的位置(min+max)/2 =mid,查看mid槽对应的主键值y,若x<y,则min不变,max=mid,若x>y,则max不变,min=mid。依此类推。
数据页
数据页之间在物理空间上并不是相连的,在每个数据页中的file header部分中有FIL_PAGE_PREV上一个页的页号、FIL_PAGE_NEXT下一个页的页号从而使数据页之间形成双向链表
索引
如果我们想从很多页中根据主键快速的定位某些记录所在的页,我们需要给他们做一个目录,每个页对应这一个目录项,每个目录项包括下面两部分:
- 页的用户记录中最小记录的主键值,我们用key表示
- 页号,我们用page_no表示
如何根据主键值快速找到某条记录
- 先从目录项中根据二分查找法快速确定出主键值对应的目录项,从而找出主键值所对应的页
- 在根据在页中查找记录的方式定位具体的记录
改进
InnoDB的设计者,复用了之前存储用户记录的数据页来存储目录项,为了和用户记录做下区分,我们把这些用来表示目录项的记录称为目录记录。record_type=1表示。
如何根据根据主键值快速定位一个存储目录项记录
为这些目录项记录的页在生成一个更高级的目录,就像一个多级目录一样,大目录里嵌套小目录。最后形成一棵B+树。
实际用户记录其实都存放在B+树的最底层节点上,即叶子节点
聚簇索引
- 使用记录主键值的大小进行记录和页的排序
- B+树的叶子节点存储的是完整的用户记录
二级索引
- 使用记录c列的大小进行记录和页的排序
- B+树的叶子节点存储的并不是完整的用户记录,而只是c列+主键值这两个列的值
为了保证除了页号之外,目录项记录唯一,所以二级索引的内节点实际上是由三部分组成:索引列的值、主键值、页号
联合索引
建立c1和c2的联合索引
- 每条目录项记录都是由c1,c2,页号这三部分组成,各条记录先按照c1列进行排序,如果c1列相同则按照c2列进行排序
- B+树叶子节点处的用户记录由c1,c2和主键组成。
MyISAM存储引擎
MyISAM将表中的记录按照记录的插入顺序单独存储在一个文件中,称之为数据文件。这个文件并不划分为若干个数据页,有多少记录就往着个文件中插入多少条记录,我们可以通过行号快速访问到一条记录。
MyISAM存储引擎的数据和索引分开存储,这种存储引擎的索引全部是二级索引,在叶子节点处存储的记录内容是列+行号。 在通过行号查找数据。
区的概念
对于16kb的页来说,连续的64个页就是一个区,也就是说一个区默认占用1MB。
InnoDB设计了一个XDES Entry的结构,每一个区都对应着一个XDES Entry结构。
把状态为free的区对应的XDES Entry结构通过List Node来连接成一个链表,即free链表,
把状态为free_frag的区对应的XDES Entry结构通过List Node来连接成一个链表,即free_frag链表,
把状态为full_frag的区对应的XDES Entry结构通过List Node来连接成一个链表,即full_frag链表。
段的概念
叶子节点有自己独有的区,非叶子节点也有自己独有的区。存放叶子节点的区的集合就是算是一个段,存放非叶子节点的区的集合也算一个段,也就是一个索引会生成2个段,一个叶子节点段,一个非叶子节点段。
段:是某些零散的页面以及一些完整的区的集合。
链表小结
表空间是由若干个区组成,每个区对应个一个XDES Entry的结构,只属于表空间的区对应的XDES Entry结构可以分成FREE、FREE_FRAG和FULL_FRAG这三个链表;每个段可以附属若干个区,每个段中的区对应的XDES Entry结构可以分成FREE、NOT_FULL和FULL这3个链表。每个链表都对应一个List Base Node的结构,这个结构记录了链表的头、尾节点的位置,以及该链表中的节点数。