B树的优点很明显:无论进行新元素的插入定位还是进行指定元素的查找,都可以快速完成这些定位/查找动作,其查询性能的时间复杂度相当于二分查找法(O(log(n)))。但是B树的缺点也很明显,首先插入新元素时可能涉及到树深度的改变,当然这个问题可以通过增加B树的阶数来解决。也就是让每一个节点可以拥有更多的子节点,这样就可以在存储元素总量不变的情况下减少树的理论深度,从而减少发生树深度改变的情况,
另一个问题就稍微严重一些了,那就是B树并不适合管理InnoDB引擎中的数据(这个在后文会进行说明)。还好B树结构有一个变体结构,称为B+树。两者最大的区别是,后者在B树的结构基础上扩展出了一个链表存储结构,并且在树的叶子节点对非叶子结点元素进行了冗余存储。如下图所示:
B树和B+树在新元素插入、元素删除、元素查找等基本处理方式上没有太大的区别。但是B+树的两个典型的结构变动刚好可以改进树结构在InnoDB引擎中的应用:
由于B+树的叶子节点存储了非叶子节点的冗余元素,所以我们可以在非叶子节点只记录某条数据的索引信息,而在叶子节点记录具体的数据信息。那么MySQL数据库就可以在InnoDB引擎启动时就加载B+树的非叶子节点到内存特定区域,这样做的最大好处是可以在内存空间和查找速度两个维度上找到***的平衡点。
特别注意的是,实际在InnoDB引擎中的B+树结构,其叶子节点并不是存储一行数据(要真是这样,这颗B+树不知道需要有多大。。。),而是和Data Page作为对应。在之前介绍InnoDB引擎的文章中已经说明过数据库中Page的概念。Page是InnoDB引擎中最基本的数据操作单元,无论是InnoDB从磁盘上读取数据还是改变数据,都以Page作为操作单位。同样,InnoDB引擎中的索引结构也以Page为单位在叶子节点关联具体数据。
另外B+树在最底层将所有叶子节点串成了一个链表结构(不用担心某个叶子节点没有任何元素,因为B+树遵循所有B树的基本约束),这样一来在进行数据查找时就可以使用表结构进行元素的依次查找,而无需再进行树的遍历操作。实际上在InnoDB引擎中每一个叶子节点都是一个Page信息,构成链表结构后就可以检索每一个Page的上一个Page和下一个Page信息,这恰好也是InnoDB引擎中预读功能的实现基础。
4-1-3、InnoDB中的索引类型InnoDB数据引擎使用B+树构造索引结构,其中的索引类型依据参与检索的字段不同可以分为主索引和非主索引;依据B+树叶子节点上真实数据的组织情况又可以分为聚族索引和非聚族索引。每一个索引B+树结构都会有一个独立的存储区域来存放,并且在需要进行检索时将这个结构加载到内存区域。真实情况是InnoDB引擎会加载索引B+树结构到内存的Buffer Pool区域。
聚簇索引(聚集索引)
聚簇索引指的是这样的数据组织结构:索引B+树的每个叶子节点直接对应了真实的Data Page。并且B+树所有的叶子节点在最底层共同描述了一个可以直接进行行数据顺序扫描的Data Page结构。如下图所示:
InnoDB引擎在组织索引和数据时,就是通过聚簇索引检索具体Data Page。而聚簇索引B+树的非叶子节点一般由数据表中的主键负责构造(当然也可能不是主键,这个后文会进行说明)。
主索引(主键索引/一级索引)
基于InnoDB引擎工作的每一张数据表都需要有一个主索引,这是因为上一段文字中提到的InnoDB引擎需要使用聚簇索引查找到具体的Data Page,而工作在InnoDB引擎下的数据表有且只有主索引采用聚簇索引的方式组织数据。也就是说主索引B+树的叶子节点都对应了真实的Data Page信息。
主索引在数据表的索引列表中使用PRIMARY关键字进行标识,一般来说是数据表的主键字段(也有可能是复合主键)。如果开发人员删除了InnoDB引擎中某张数据表的主索引,那么这个数据表将自行寻找一个非空且带有唯一约束的字段作为主索引。如果还是没有找到那样的字段,InnoDB引擎将使用一个隐含字段作为主索引(ROWID)。