本篇介绍下Mysql的InnoDB索引相关知识,从各类树到索引原理到存储的细节。算法
InnoDB是Mysql的默认存储引擎(Mysql5.5.5以前是MyISAM,文档)。本着高效学习的目的,本篇以介绍InnoDB为主,少许涉及MyISAM做为对比。sql
这篇文章是我在学习过程当中总结完成的,内容主要来自书本和博客(参考文献会给出),过程当中加入了一些本身的理解,描述不许确的地方烦请指出。数据库
1 各类树形结构数据结构
原本不打算从二叉搜索树开始,由于网上已经有太多相关文章,可是考虑到清晰的图示对理解问题有很大帮助,也为了保证文章完整性,最后仍是加上了这部分。性能
先看看几种树形结构:学习
1 搜索二叉树:每一个节点有两个子节点,数据量的增大必然致使高度的快速增长,显然这个不适合做为大量数据存储的基础结构。测试
2 B树:一棵m阶B树是一棵平衡的m路搜索树。最重要的性质是每一个非根节点所包含的关键字个数 j 知足:┌m/2┐ - 1 <= j <= m - 1;一个节点的子节点数量会比关键字个数多1,这样关键字就变成了子节点的分割标志。通常会在图示中把关键字画到子节点中间,很是形象,也容易和后面的B+树区分。因为数据同时存在于叶子节点和非叶子结点中,没法简单完成按顺序遍历B树中的关键字,必须用中序遍历的方法。优化
3 B+树:一棵m阶B树是一棵平衡的m路搜索树。最重要的性质是每一个非根节点所包含的关键字个数 j 知足:┌m/2┐ - 1 <= j <= m;子树的个数最多能够与关键字同样多。非叶节点存储的是子树里最小的关键字。同时数据节点只存在于叶子节点中,且叶子节点间增长了横向的指针,这样顺序遍历全部数据将变得很是容易。3d
4 B*树:一棵m阶B树是一棵平衡的m路搜索树。最重要的两个性质是1每一个非根节点所包含的关键字个数 j 知足:┌m2/3┐ - 1 <= j <= m;2非叶节点间添加了横向指针。指针
B/B+/B*三种树有类似的操做,好比检索/插入/删除节点。这里只重点关注插入节点的状况,且只分析他们在当前节点已满状况下的插入操做,由于这个动做稍微复杂且能充分体现几种树的差别。与之对比的是检索节点比较容易实现,而删除节点只要完成与插入相反的过程便可(在实际应用中删除并非插入的彻底逆操做,每每只删除数据而保留下空间为后续使用)。
先看B树的分裂,下图的红色值即为每次新插入的节点。每当一个节点满后,就须要发生分裂(分裂是一个递归过程,参考下面7的插入致使了两层分裂),因为B树的非叶子节点一样保存了键值,因此已满节点分裂后的值将分布在三个地方:1原节点,2原节点的父节点,3原节点的新建兄弟节点(参考5,7的插入过程)。分裂有可能致使树的高度增长(参考3,7的插入过程),也可能不影响树的高度(参考5,6的插入过程)。
1 每一个叶子节点存储了468行数据,每一个非叶子节点存储了大约1200个键值,这是一棵平衡的1200路搜索树!
2 对于一个22.1G容量的表,也只须要高度为3的B+树就能存储了,这个容量大概能知足不少应用的须要了。若是把高度增大到4,则B+树的存储容量马上增大到25.9T之巨!
3 对于一个22.1G容量的表,B+树的高度是3,若是要把非叶节点所有加载到内存也只须要少于18.8M的内存(如何得出的这个结论?由于对于高度为2的树,1203个叶子节点也只须要18.8M空间,而22.1G从良表的高度是3,非叶节点1204个。同时咱们假设叶子节点的尺寸是大于非叶节点的,由于叶子节点存储了行数据而非叶节点只有键和少许数据。),只使用如此少的内存就能够保证只须要一次磁盘IO操做就检索出所需的数据,效率是很是之高的。
能够说数据库必须有索引,没有索引则检索过程变成了顺序查找,O(n)的时间复杂度几乎是不能忍受的。咱们很是容易想象出一个只有单关键字组成的表如何使用B+树进行索引,只要将关键字存储到树的节点便可。当数据库一条记录里包含多个字段时,一棵B+树就只能存储主键,若是检索的是非主键字段,则主键索引失去做用,又变成顺序查找了。这时应该在第二个要检索的列上创建第二套索引。 这个索引由独立的B+树来组织。有两种常见的方法能够解决多个B+树访问同一套表数据的问题,一种叫作聚簇索引(clustered index ),一种叫作非聚簇索引(secondary index)。这两个名字虽然都叫作索引,但这并非一种单独的索引类型,而是一种数据存储方式。对于聚簇索引存储来讲,行数据和主键B+树存储在一块儿,辅助键B+树只存储辅助键和主键,主键和非主键B+树几乎是两种类型的树。对于非聚簇索引存储来讲,主键B+树在叶子节点存储指向真正数据行的指针,而非主键。
InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法便可查找到对应的叶节点,以后得到行数据。若对Name列进行条件搜索,则须要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操做,最终到达叶子节点便可获取整行数据。
MyISM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不一样,节点的结构彻底一致只是存储的内容不一样而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来讲,这两个键没有任何差异。因为索引树是独立的,经过辅助键检索无需访问主键的索引树。
为了更形象说明这两种索引的区别,咱们假想一个表以下图存储了4行数据。其中Id做为主索引,Name做为辅助索引。图示清晰的显示了聚簇索引和非聚簇索引的差别。
1 因为行数据和叶子节点存储在一块儿,这样主键和行数据是一块儿被载入内存的,找到叶子节点就能够马上将行数据返回了,若是按照主键Id来组织数据,得到数据更快。
2 辅助索引使用主键做为"指针" 而不是使用地址值做为指针的好处是,减小了当出现行移动或者数据页分裂时辅助索引的维护工做,使用主键值看成指针会让辅助索引占用更多的空间,换来的好处是InnoDB在移动行时无须更新辅助索引中的这个"指针"。也就是说行的位置(实现中经过16K的Page来定位,后面会涉及)会随着数据库里数据的修改而发生变化(前面的B+树节点分裂以及Page的分裂),使用聚簇索引就能够保证无论这个主键B+树的节点如何变化,辅助索引树都不受影响。
3 Page结构
若是说前面的内容偏向于解释原理,那后面就开始涉及具体实现了。
理解InnoDB的实现不得不提Page结构,Page是整个InnoDB存储的最基本构件,也是InnoDB磁盘管理的最小单位,与数据库相关的全部内容都存储在这种Page结构里。Page分为几种类型,常见的页类型有数据页(B-tree Node)Undo页(Undo Log Page)系统页(System Page) 事务数据页(Transaction System Page)等。单个Page的大小是16K(编译宏UNIV_PAGE_SIZE控制),每一个Page使用一个32位的int值来惟一标识,这也正好对应InnoDB最大64TB的存储容量(16Kib * 2^32 = 64Tib)。一个Page的基本结构以下图所示:
1 经过根节点开始遍历一个索引的B+树,经过各层非叶子节点最终到达一个Page,这个Page里存放的都是叶子节点。
2 在Page内从"Infimum"节点开始遍历单链表(这种遍历每每会被优化),若是找到该键则成功返回。若是记录到达了"supremum",说明当前Page里没有合适的键,这时要借助Page的Next Page指针,跳转到下一个Page继续从"Infimum"开始逐个查找。
1 主索引树非叶节点(绿色)
1 子节点存储的主键里最小的值(Min Cluster Key on Child),这是B+树必须的,做用是在一个Page里定位到具体的记录的位置。
2 最小的值所在的Page的编号(Child Page Number),做用是定位Record。
2 主索引树叶子节点(黄色)
1 主键(Cluster Key Fields),B+树必须的,也是数据行的一部分
2 除去主键之外的全部列(Non-Key Fields),这是数据行的除去主键的其余全部列的集合。
这里的1和2两部分加起来就是一个完整的数据行。
3 辅助索引树非叶节点非(蓝色)
1 子节点里存储的辅助键值里的最小的值(Min Secondary-Key on Child),这是B+树必须的,做用是在一个Page里定位到具体的记录的位置。
2 主键值(Cluster Key Fields),非叶子节点为何要存储主键呢?由于辅助索引是能够不惟一的,可是B+树要求键的值必须惟一,因此这里把辅助键的值和主键的值合并起来做为在B+树中的真正键值,保证了惟一性。可是这也致使在辅助索引B+树中非叶节点反而比叶子节点多了4个字节。(即下图中蓝色节点反而比红色多了4字节)
3 最小的值所在的Page的编号(Child Page Number),做用是定位Record。
4 辅助索引树叶子节点(红色)
1 辅助索引键值(Secondary Key Fields),这是B+树必须的。
2 主键值(Cluster Key Fields),用来在主索引树里再作一次B+树检索来找到整条记录。
1 原理是基石,只有充分了解InnoDB索引的工做方式,咱们才有能力高效的使用好它。
2 原理性知识特别适合使用图示,我我的很是喜欢这种表达方式。
3 关于InnoDB优化,在《高性能Mysql》里有更加全面的介绍,对优化Mysql感兴趣的同窗彻底能够本身获取相关知识,我本身的积累还未达到能分享这些内容的地步。