InnoDB引擎中的索引与算法

5.1 InnoDB支持以下几种常见的索引:

  • B+树索引
  • 全文索引
  • 哈希索引(自适应哈希索引)
    • 关于哈希索引的说明:
    -- 1、InnoDB的哈希索引是自适应的,其根据表的使用情况自动生成哈希索引,不能人为干预是否在一张表中生成哈希索引。

    -- 2、自适应哈希索引是由InnoDB自己控制的,可以通过innodb_adaptive_hash_index来禁用或者启动此特性,默认为开启。

    -- 3、而你在建立索引时,选择的索引方法中有B+tree 和 hash, 这里的hash并不是真的hash索引,实际还是Btree索引,

    -- 以下为例子:

          CREATE TABLE `test_hash` (
          `id` int(11) NOT NULL,
          `name` varchar(255) DEFAULT NULL,
          `email` varchar(255) DEFAULT NULL,
          PRIMARY KEY (`id`),
          UNIQUE KEY `hash_index` (`email`) USING HASH,
          KEY `B_index` (`name`) USING BTREE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     -- DDL中是HASH索引,然而我们通过 SHOW INDEXES FROM test_hash; 命令查询可以看到其index_type为BTREE。
- 关于B+树索引的说明:
    1、B+树索引就是传统意义上的索引,是目前关系型数据库中查找最为常用和最有效的索引。B+树索引的构造类似于二叉树,根据键值(Key Value)快速找到数据。

    2、B+树中的B不是代表二叉(binary),而是代表平衡(balance),B+树是从最早的平衡二叉树演化而来,但B+树不是一个二叉树。

    3、B+树索引并不能找到一个给定键值的具体行,能找到的只是该数据行所在的页。然后数据库把页读到内存中,在内存中查找到需要的数据。

5.2 数据结构与算法

       接下来会介绍一些与B+树索引相关的算法与数据结构,以帮助大家更好的理解B+树索引的工作方式。

5.2.1 二分查找法
  •        二分查找也称为折半查找法,用来查找一组有序的记录数组中的某个记录,其基本思想是:将记录按照有序化(递增或递减)排列,在查找过程中先以有序数列的中点位置为比较对象,如果要找的元素值小于该中点元素,则将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。
  •        如有5、10、19、21、31、37、42、48、50、55这10个数,先要从这10个数中查找48这条记录,其查找过程如图5-1所示。
  •        从图5-1可以看出,用了3次就找到了48这个数,如果是顺序查找,则需要8次。因此二分查找法的效率要比顺序查找法好(平均的来说)。但如果说查5这条记录,顺序查找只需1次,二二分查找需要4次。对于上面10个数来说,平均查找次数为(1+2+3+4+5+6+7+8+9+10)/10=5.5次。而二分查找法为(4+3+2+4+3+1+4+3+2+3)/10=2.9次。但最坏的情况下,顺序查找需要10次,而二分查找需要4次。
  •        二分查找法的应用及其广泛,而且它的思想易于理解。Page Directory 中的槽是按照主键的顺序存放的,对于某一条具体记录的查询时通过对Page Directory进行二分查找得到的,官网中是这样说的:Mysql在定位到Page后在根据slot进行二分查找。
5.2.2 二叉查找树和平衡二叉树
  •        在介绍B+树前,需要先了解一下二叉查找树。B+树是通过二叉查找树,再由平衡二叉树,B树演化而来。二叉查找树是一种经典的数据结构。图5-2显示了一颗二叉查找树。

  •        在二叉查找树中,左子树的键值总小于根的键值,右子树的键值总大于根的键值。因此可以通过中序遍历得到键值的排序输出,图5-2的二叉查找树经过中序遍历后输出:2、3、5、6、7、8.

  •        对于图5-2,如查找5,先找到根,根键值为6大于5,因此查找6的左子树,找到3;而5大于3,再找右子树;一共找了3次。如果按2、3、5、6、7、8的顺序来找同样需要3次。用同样的方法再次查找键值为8的记录,这次用了3次查找,而顺序查找需要6次。计算平均查找次数可得:顺序查找的平均查找次数为(1+2+3+4+5+6)/6=3.3次, 二叉查找树的平均查找次数为(3+3+3+2+2+1)/6=2.3次。二叉查找树的平均查找速度比顺序查找更快。

  •        二叉查找树可以任意的构造,同样是2、3、5、6、7、8这五个数字,也可以按照图5-3的方式建立二叉查找树。

  •        图5-3的平均查找次数为(1+2+3+4+5+5)/6=3.16次,和顺序查找差不多。显然这可二叉查找树的效率就比较低了。因此弱项最大性能地构造一颗二叉查找树,需要这颗二叉查找树是平衡的,从而引出了新的定义 —— 平衡二叉树,或称为AVL树。

  •        平衡二叉树的定义如下:首先符合二叉查找树的定义,其次必须满足任何节点的两个子树的高度最大差为1。显然,图5-3不满足平衡二叉树的定义,而图5-2是一颗平衡二叉树。当然性能最高的不是平衡二叉树,而是最优二叉树,但最优二叉树的建立与维护需要大量的操作,因此,一般我们只需建立一颗平衡二叉树即可。

  •        平衡二叉树的查询速度的确很快,但是维护一颗平衡二叉树的代价是非常大的。通常来说,需要1次或多次的左旋和右旋来得到插入或更新后树的平衡性。对于图5-2所示的平衡术,当用户需要插入一个新的键值为9的节点时,需做图5-4所示的变动。

  •         这里通过一次左旋操作就将插入后的树重新变为平衡的了。但是有的情况可能需要旋转多次,如图5-5所示。

  • 【左旋转】
    也就是逆时针旋转两个节点,使父节点被自己的右孩子取代,而自己成为自己的左孩子

  • 【右旋转】
    顺时针旋转两个节点,使得自己的父节点被左孩子取代,而自己成为自己的右孩子

  • 插入、更新和删除操作类似,都是通过左旋右旋来完成的。因此对一颗平衡树的维护是有一定开销成本的,不过平衡二叉树多用于内存结构的对象中,因此维护的开销相对较小。相对而言,B-树结构是为磁盘或者其他直接存取的辅助设备设计的一种平衡查找树

B-树 (读作B树)

  • 从算法逻辑上来讲,二叉查找树的查找速度和比较次数都是最小的。但是二叉树的高度要比B-树高,在mysql中,树的高度代表了磁盘IO的次数。

  • 数据库索引是存储在磁盘上的,当数据量比较大的时候,索引的大小可能有几个G甚至更多,要知道现在innodb默认设置数据与索引存储在同一个.idb文件中。

  • 当我们利用索引查询的时候,显然不可能将整个索引加载到内存中,只能逐一加载每个磁盘页,这里的磁盘页对应着索引树的节点。

  • 刚才我们讲了树的高度代表了磁盘IO的次数,所以我们就需要把原本“瘦高的”树结构变得“矮胖”。这就是B-树的特征之一。

  • B-树是一种多路平衡查找树,它的每一个节点最多包含k个孩子,K被称为B-树的阶。K的大小取决于磁盘页的大小。

一个m'阶的B-数具有如下几个特征:
  • 1、根节点至少有两个孩子。
  • 2、每个中间节点都包含K-1个元素和K个孩子,其中m/2 <= k <=m
  • 3、每一个叶子节点都包含K-1个元素,其中 m/2 <= k <= m
  • 4、所有的叶子节点都位于同一层
  • 5、每个节点中的元素从小到大排列,节点当中的k-1个元素正好是K个孩子包含的元素的值域划分。
下面看一个B-树

  • 这颗树中,咱们重点来看(2,6)节点。该节点有两个元素2和6,又有三个孩子1,(3,5),8。其中1小于元素2,然后(3,5)在元素2,6之间,8大于6.
  • 下面来演示下B-树的查询过程,假如我们要查找的数值是5

  • 整个流程可以看出, B-树在查询中的比较次数并不比二叉树少,尤其当单一节点中的元素数量很多时。

  • 可是相比磁盘IO的速度,内存中的比较耗时几乎可以忽略。所以只要树的高度足够低,IO的次数就足够少,就可以提升查找性能。

  • 节点内部的元素多一些也没有关系,仅仅是多了几次内存交互,只要不超过磁盘页的大小即可。这就是B-树的优势之一。

  • 切换节点,相当于从磁盘中新取出一页数据加载到内存中,而比较大小就是在内存中直接比较就可以了。

B-树插入新节点
  • B-树的插入新节点比较复杂,而且分很多情况,这里我们只将一个最典型的例子,假设我们要插入的值是4

  • 自顶向下查找4的节点位置,发现4应当插入到节点元素3,5之间。

  • 节点3,5已经是两元素节点,无法再增加。父亲节点 2, 6 也是两元素节点,也无法再增加。根节点9是单元素节点,可以升级为两元素节点。于是拆分节点3,5与节点2,6,让根节点9升级为两元素节点4,9。节点6独立为根节点的第二个孩子。

  • 虽然很麻烦,但B-树始终能维持多路平衡,这也是B-树的一大优势:自平衡

B-树的删除
  • 同样只举一个最经典的例子,删除 11

  • 首先,自顶向下查找元素11的节点位置。

  • 删除11后,节点12只有一个孩子,不符合B树规范。因此找出12,13,15三个节点的中位数13,取代节点12,而节点12自身下移成为第一个孩子。(这个过程称为左旋)

  • B-树主要应用于文件系统以及部分数据库索引,比如MongoDB。

  • 大部分关系型数据库,比如MySQL使用B+树作为索引。

B+树

  • B+树和二叉树、平衡二叉树一样,都是经典的数据结构。
B+树是基于B-树的一种变体,有着比B-树更高的查询性能,一个m阶的B+树有如下几个特征。
  • 1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

  • 2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

  • 3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

  • 从图中我们可以发现,每个父节点的元素都会出现在子节点中,是子节点的最大(或最小)元素。

  • 根节点的最大元素(是15),也就等同于整个B+树的最大元素。以后无论插入删除多少元素,始终要保持最大元素在根节点中。

  • 由于父节点都会出现在子节点,所以叶子节点中包含了所有元素的信息,并且每个叶子节点都有指向下一个节点的指针,形成了有序链表。

  • 相对比B-数,B+数只有叶子节点才有卫星数据,而B-树中所有节点都包含卫星数据。B+树中的中间节点仅仅是索引,没有任何数据关联。

  • 卫星数据是:指的是索引元素所指向的数据记录,比如数据库中的某一行。

  • innoDB中的聚集索引和非聚集索引虽然都是B+树结构,但是还有区别:

    • 聚集索引中,叶子节点直接包含卫星数据(可以直接获取到数据)
    • 非聚集索引中,叶子节点带有指向卫星数据的指针 (需要根据指针再去查询)
  • B+树的好处主要体现在查询性能上。

  • 查询过程跟B-树一样,但是B+树的中间节点只是索引没有数据,所以同样大小的磁盘页可以容纳更多的节点元素。这意味着,数据量相同的情况下,B+树的结构更加“矮胖”,因此IO次数会更少。

  • 其次B+树的查询比如最终查找到叶子节点,而B-树只要找到匹配元素即可,因此B-树的查找性能并不稳定,B+树的性能是稳定的。

  • 再看范围查询,B-树如何做范围查询呢?只能靠中序遍历,而B+树就不一样了,直接在叶子节点的链表上遍历即可。

  • 综上所述:B+树比B-树的IO次数更少,性能更稳定,范围查询更便利。 至于删除跟B-树大同小异,主要还是左旋右旋。

posted @ 2019-12-12 13:25  _Eternity味道  Views(402)  Comments(0Edit  收藏  举报