0
点赞
收藏
分享

微信扫一扫

MySQL索引之B+数索引详解

一、MySQL索引定义

索引(Index)是帮助MySQL高效获取数据的数据结构,本质就是数据结构InnoDB存储引擎支持以下几种常见的索引:

B+树索引、全文索引、哈希索引,其中比较关键的是B+树索引。

二、为什么不用HashMap做数据库索引呢

HashMap查询/插入/修改/删除的平均时间复杂度都是O(1),而平衡二叉搜索树,查询/插入/修改/删除的平均时间复杂度都是O(lg(n)),为什么不用HashMap做数据库索引呢?

  1. hash表只能匹配是否相等,不能实现范围查找(如果单行查找,如:select * from table where id = xxx 此语句确实是哈希索引更快,因为每次都只查询一条记录)
  2. 当需要按照索引进行order by、group by、<、>时,hash值没办法支持排序(此时哈希型的索引,时间复杂度会退化为O(n),而树型的“有序”特性,依然能够保持O(log(n)) 的高效率。
  3. 组合索引可以支持部分索引查询,如(a,b,c)的组合索引,查询中只用到了a和b也可以查询的,如果使用hash表,组合索引会将几个字段合并hash,没办法支持部分索引
  4. 当数据量很大时,hash冲突的概率也会非常大

三、B+Tree

  1. B+树索引就是传统意义上的索引,这是目前关系型数据库系统中查找最常用和最为有效的索引。B+树索引的构造类似于二叉树,根据键值(Key Value)快速找到数据。注意B+树中的B不是代表二叉(binary),而是代表平衡(balance),因为B+树是从最早的平衡二叉树演化而来,但是B+树不是一个二叉树。
  2. 前置知识
  • 二叉查找(搜索)树(搜索二叉树不接收重复的key,每个key都不一样,对于重复的key直接更新当前此节点的value即可)
  1. 每个节点最多棵子树
  2. 所有左子树的所有值小于根节点的值
  3. 所有右子树的所有值大于或等于根节点的值
  4. 二叉查找树,如果设计不良,完全可以变成一颗极不平衡的二叉查找树,所以出现了平衡二叉树,下图为极不平衡二叉树:

MySQL索引之B+数索引详解_平衡二叉树

  • 平衡二叉树(AVL-树)
  1. 是一棵二叉排序树
  2. 左右两个子树的高度差(平衡因子)的绝对值不超过1(使得树的高度最低,因为树查找的效率决定于树的高度),并且左右两个子树都是一棵平衡二叉树
  3. 平衡二叉树的查找性能是比较高的,但是维护一棵平衡二叉树的代价是非常大的。通常来说,需要1次或多次左旋和右旋来得到插入、更新和删除后树的平衡性。

平衡二叉树详解参考连接: https://blog.51cto.com/u_14291296/6193890

  • B+树
  • 一棵m阶B+ 树定义
  • 每个非叶子节点最多有m个子节点,其中m>=2
  • 每个非叶子节点最少有ceil(m/2)个子节点,即向上取整(m/2)
  • 所有叶子节点都在同一层级,形成一个链表,便于范围查询
  • 所有非叶子节点都没有存储数据,只存储了关键字和指向子节点的指针
  • 每个节点最多包含m-1个关键字,且关键字按照升序排列
  • 每个节点最少包含ceil(m/2)-1个关键字,即向上取整((m-1)/2)个
  • B+ 树特点
  • 由平衡二叉树演化而来,但B+树不是二叉树,而是一个多叉查找平衡树
  • 树的阶数表示一个节点最多能有多少个子节点
  • 每个叶子页(LeafPage)存储了实际的数据,叶子节点由小到大(有序)串联在一起,叶子页中的数据也是排好序的
  • 相同节点数量的情况下,B+树高度远低于平衡二叉树(因为多叉)
  • 非叶子节点只保存索引信息和下一层节点的指针信息,不保存实际数据记录(但是B树即B-树保存实际数据记录)
  • 相邻的叶子节点之间用指针相连

注意:叶子节点中的数据在物理存储上完全可以是无序的,仅仅是在逻辑上有序(通过指针串在一起)

快速生成B+树网站: https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

如下为一棵4阶B+树:

MySQL索引之B+数索引详解_子节点_02

四、MySQL存储时,为什么不选B(B-)树、B*树,而选择B+树?

首先排除,B+比B和B-等级高一级的说法。

为什么用B+树而不用B树:
  1. 更适合磁盘I/O访问:相比于B树,B+树的内部节点不存储数据,只存储指向子节点的指针,因此每个节点可以存储更多的关键字,使得B+树的高度更低,从而减少磁盘I/O操作的次数。此外,B+树的叶子节点组成了一个链表,便于范围查询,从而进一步提高了查询效率。
  2. 更适合数据库索引:B+树的叶子节点存储了完整的数据记录,因此在查询时,只需要遍历叶子节点即可获取到完整的数据记录。而B树的叶子节点只存储了关键字和指向数据记录的指针,还需要根据指针进行额外的访问才能获取到完整的数据记录,这会增加磁盘I/O操作的次数,从而降低查询效率。
  3. 支持高效的范围查询:由于B+树的叶子节点形成了一个链表,因此可以非常高效地进行范围查询操作,而B树的叶子节点并没有形成链表,需要进行递归遍历才能完成范围查询操作,效率较低。

如下为一棵4阶B树:

MySQL索引之B+数索引详解_平衡二叉树_03

为什么用B+树而不用B*树

B树与B+树的差别就是B在非叶子节点之间,也有相互的指针指向,有指针就意味着需要更多的空间去存储,而且叶子节点之间的构成的指针链表就能满足范围查询需要了,所以使用B+树更加简单,还能减少空间消耗,但是Oracle使用的是B*树。

总结

关系型数据库都选择了B+树,这个和磁盘的特性有着非常大的关系。

  1. 为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存,这个称之为预读
  2. 预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页,操作系统的页大小通常为4k。
  3. 按照磁盘的这种性质,如果是一个页存放一个B+树的节点,可以存放很多的数据,比如InnoDB里,默认定义的B+树的节点大小是16KB,这就是说,假如一个Key是8个字节,那么一个节点可以存放大约1000个Key(别忘了B+树的节点中还需要存放指向子节点的指针,这些指针的大小通常也是8个字节),意味着B+树约可以有1000个分叉。同时InnoDB每一次磁盘I/O,读取的都是 16KB的整数倍的数据。也就是说InnoDB在节点的读写上是可以充分利用磁盘顺序IO的高速读写特性。
  4. MySQL优化方向:尽可能的多让数据顺序读写,少让数据随机读写。磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),一般来说,磁盘的顺序读的效率是随机读的40到400倍都有可能,顺序写是随机写的10到100倍。
  5. B+树含有非常高的扇出(fanout),通常超过100,在查找一个记录时,可以有效的减少IO操作;

扇出:是每个索引节点(Non-LeafPage)指向每个叶子节点(LeafPage)的指针

扇出数 = 索引节点(Non-LeafPage)可存储的最大关键字个数 + 1

举报

相关推荐

0 条评论