1、前言
innerdb 读取磁盘记录的时候,肯定不是一条记录一条记录的从磁盘中读取,而是以页为单位读取记录。
innerdb 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小
一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB 内容刷新到磁盘中。
2、数据页结构
mysql 为什么叫行式数据库,因为它的记录是按照行来存储记录的。innerdb 的页结构如下图所示:
页面结构
一般数据记录存储在 User Records 里面。开始 User Records 是没有值的,随着记录越插越多, User Records 使用的空间越来越多,Free Space 会慢慢减小直到0,最后整个页都满了。
还有就是,mysql 的数据被删除了,他的空间不一定被释放,因为它的空间是以页为单位存储的,只有整个页被删除了,空间才会释放。
数据页的 Infimum 和 supremum 是两个伪记录,Infimum 主要指向 User Records 中的最小记录,supremum 指向 User Records 中的最大记录。
数据页中有 Page Directory 属性,记录了 User Records 中每个分组的最大值。也就是将 User Records 分组,Page Directory 每个组中的最大值,在查找的时候从 Page Directory 查数据属于哪一组,然后再遍历查找。Page Directory 记录的分组信息也叫做槽。
数据页的 Page Header 属性主要记录页中的槽数量、记录数量等等。
数据页的 File Header 有两个比较重要的属性,分别为 FIL_PAGE_PREV 和 FIL_PAGE_NEXT,这两个属性分别记录指向前面的页的地址与指向后面页的地址。还有一个比较重要的就是校验和。
数据页的 File Tailer 主要存储原来记录的校验和。如果一个数据页被更改了,那么 File Header、File Tailer 中的校验和会改成最新的,但是磁盘中的这两个部分的校验和都是原来的。当数据页往磁盘同步时,因为时先写头后面在写尾的,如果通过过程出错,那么 File Header 的校验和事新的,File Tailer 是原来的,那么校验和不一致。innerdb 用这种方法判断一个页是否完整。
3、索引结构
在前面我们知道,每个数据页会通过 File Header 的双向链表进行链接,每个数据页中的 User Records 记录也会通过单向链表进行链接,并且数据页会有一个 Page Directory 记录每个数据页的分组中的最大值。
那么针对数据页的目录怎么办呢?innerdb 复用了原来的数据页的结构,根据 record_type 区分 User Records 中的数据是普通数据还是标识索引数据(record_type 在每一行记录的头那里),索引数据只有每页的最小主键值和页号,主键值记录的是每个数据页的最小主键。
innerdb 的主键索引
如果记录特别说,一个数据页的目录项放不下这些记录,那么会继续分裂,生成更多的目录,就像 B+ 树一样,只不过跟 B+ 树有点不同的是,B+ 树的非叶子结点不一定会记录每个叶子结点的最小值,而这边需要。
说完了主键索引,那么非主键索引呢?
非主键索引的结构类似,只是它的叶子结点的值不再是完整的数据,而是索引列的值 + 主键的值。针对数据页的记录变成了索引列的值 + 页号 + 主键的值。
非主键索引
4、总体流程
我们前边介绍 B+ 树索引的时候,为了大家理解上的方便,先把存储用户记录的叶子节点都画出来,然后接着画存储目录项记录的内节点,实际上 B+ 树的形成过程是这样的:
1)每当为某个表创建一个 B+ 树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引创建一个 根节点 页面。最开始表中没有数据的时候,每个 B+ 树索引对应的 根节点 中既没有用户记录,也没有目录项记录。
2)随后向表中插入用户记录时,先把用户记录存储到这个 根节点 中。
3)当 根节点 中的可用空间用完时继续插入记录,此时会将 根节点 中的所有记录复制到一个新分配的页,比如 页a 中,然后对这个新页进行 页分裂 的操作,得到另一个新页,比如 页b 。这时新插入的记录根据键值(也就是聚簇索引中的主键值,二级索引中对应的索引列的值)的大小就会被分配到 页a 或者 页b 中,而
根节点 便升级为存储目录项记录的页。这个过程需要大家特别注意的是:一个B+树索引的根节点自诞生之日起,便不会再移动。这样只要我们对某个表建立一个索引,那么它的 根节点 的页号便会被记录到某个地方,然后凡是 InnoDB 存储引擎需要用到这个索引的时候,都会从那个固定的地方取出 根节点 的页号,从而来访问这个索引。
5、后记
为列建立索引时,最好找区分度大的列,即列中不同的值越多,索引效果越好
网友评论