InnoDB 索引实现

总述:

  • 表数据文件本身就是按照B+Tree组织的一个索引结构文件
  • 聚集索引-叶节点包含了完整的数据记录
  • InnoDB表必须建主键,并且推荐使用整型的自增主键
  • 非主键索引结构叶子节点存储的是主键值

少用HASH创建索引的原因:

  • 仅能满足=IN,不支持范围查找
  • 有HASH冲突
    尽管,对索引的key进行一次hash就可能定位到数据,而且很多时候HASH索引比B+树索引更高效

B-树

基本概念

树中所有结点中孩子结点个数(度)的最大值成为树的阶,通常用m表示。如图,F节点为叶子节点,度为0;B节点只有一个孩子节点D,B的度为1;C有两个孩子节点E和F,C的度为2;整棵树度最大为3(D节点),所以,这棵树的阶m=3。

一棵m阶B-树或者是一棵空树,或者是满足以下条件的m叉树:
1)每个结点最多有m个分支(子树);而最少分支数要看是否为根结点,如果是根结点且不是叶子结点,则至少要有两个分支,非根非叶结点至少有ceil(m/2)个分支,这里ceil代表向上取整。
2)如果一个结点有n-1个关键字,那么该结点有n个分支。这n-1个关键字按照递增顺序排列。
3)每个结点的左子树,关键字全部小于该节点的关键字;每个结点的右子树,关键字全部大于该节点的关键字;
4)结点内各关键字互不相等且按从小到大排列。
5)叶子结点处于同一层;可以用空指针表示,是查找失败到达的位置。

5阶树

根据以上5阶B树进行特点描述,其中最底层叶子节点没有展示:

  1. 其中有个节点有5个子树,为最大度,所以判定为5阶树;
  2. 如果根节点没有关键字,那么就是一个空树;但有一个关键字(其值为30),所以其子树必定为2,因为分支数等于关键字个数+1,但又是根节点,分支至少有2个。
  3. 因为是5阶B树,所以非根非叶节点至少有ceil(5/2)=3个分支。
  4. 除根节点之外,节点关键字个数至少为2,因为分支数至少为3,且分支数比关键字个数多1。
  5. 节点中的关键字都是有序的,且同一层中,左边节点中的关键字均小于右边节点内的关键字。
  6. 下层节点内的关键字取值总是落在由上层节点关键字所划分的区间内,如第二层最左边的节点内的关键字划分了三个区间,<15,15到26, >26,其下层中最左边结点内的关键字都小于15,中间结点的关键字在15和26之间,右边结点的关键字大于26。

注:平衡m叉查找树是指每个关键字的左侧子树与右侧子树的高度差的绝对值不超过1的查找树,其结点结构与上面提到的B-树结点结构相同,由此可见,B-树是平衡m叉查找树,但限制更强,要求所有叶结点都在同一层。

为了将大型数据库文件存储在硬盘上,以减少访问硬盘次数为目的,在此提出了一种平衡(B: balance)多路查找树——B-树结构,它能够在单个节点中存储大量键,并且通过保持树的高度相对较小来存储大键值。由其性能分析可知它的检索效率是相当高的,为了提高 B-树性能’还有很多种B-树的变型,力图对B-树进行改进,比如B+树。

B树在SQL查询中的逻辑

在以上5阶B数的27查询过程中:

  1. 根据根节点找到根节点所在磁盘块,读入内存,进行第一次磁盘I/O操作;
  2. 27比根节点30小,得到左子树的指针p1;
  3. 根据指针p1找到左子树根节点的磁盘块,读入内存,进行第二次磁盘I/O操作;
  4. 比较关键字27,大于该节点所有关键字,得到该节点的右子树指针p2;
  5. 根据p2得到27、28所在磁盘块,读入内存,进行第三次磁盘I/O操作;
  6. 在该磁盘块中找到关键字27。

分析上面过程,进行了三次磁盘I/O操作,三次内存查找。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率,并且每次磁盘I/O取到内存的数据都发挥了作用。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。

B+树

相比于B数,B+树有以下特点:

  • 非叶子节点不存储数据,只存储索引,可以存放更多的索引
  • 叶子节点包含所有索引字段,且暗含排序
  • 叶子节点使用指针链接,提高区间访问性能
B+树结构示意图

通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在24层。mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要13次磁盘I/O操作。

数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。以上B+树示意图可以认为是一个聚集索引,叶节点data存放的是完整行记录。

此外,由于B+树节点中关键字有自动排序的特性,所以,建表的时候,应该为表创建一个自增的主键,这样,在插入数据的时候,最新的数据由于主键最大,会自动在数的最右侧叶子节点加入;整数,在内存中进行二分查找可以提高效率。

联合索引

联合索引原理示意图

以上是一个以B树为存储结构的联合索引结构示意图,绿色部分为索引('name','age','position'),紫色部分为其余数据。

最左前缀法则

在联合索引中,要遵循最左前缀法则,即查询从索引的最左前列开始且不跳过索引中的列。

EXPLAIN SELECT * FROM tablez WHERE name = 'Bill' AND age = 30;
EXPLAIN SELECT * FROM tablez WHERE age = 30 AND position = 'dev';
EXPLAIN SELECT * FROM tablez WHERE position = 'dev';

第一句有索引,二三句没有使用到索引。第一句的查询过程为:

  1. 先进行name关键字的值比较,找到了左子树;
  2. 在左子树中,因为name值都相等,然后使用age关键字进行比较,得到了第一个元素;

如果age字段也有多个相等的,且查询条件有使用position关键字,然后则需要进一步比较。联合索引中,查询时关键字依次作为限定条件进行查询。

所以,二三句之所以没有使用索引,原因如下:
如果查询条件一开始就开始查询age,那么会在左子树和右子树中同时找到age=30的记录,实际进行了全表查找。

少用HASH索引

HASH值是一个映射关系,进行等值比较有很突出的优势,但是,如果是比较,HASH值并没有一个固定的升序或者降序关系,所以,在区间查询并没有任何优势。然而,在B树中,由于节点中关键字本身的排序特性以及循环双向链的叶节点结果,范围查找有天然优势。

你可能感兴趣的:(InnoDB 索引实现)