从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择

从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择

  • 1、Hash 结构
    • 1.1、关于 Hash 数据结构
    • 1.2、InnoDB索引为啥不选 Hash 结构
    • 1.3、关于InnoDB 提供自适应 Hash 索引 (Adaptive Hash Index)
  • 2、二叉搜索树
  • 3、平衡二叉树(AVL树 )
  • 4、B-Tree(B树)
  • 5、B+Tree (B+树)
    • 5.1、B+Tree 结构
    • 5.2、B+Tree 相关思考题

       数据结构可以说是程序员常用,但大部分人不知其所以然的知识点,本文将对Hash、二叉搜索树、平衡二叉树、B-Tree、B+Tree几种数据做一下梳理和总结。
 

1、Hash 结构

 

1.1、关于 Hash 数据结构

 
Hash 本身是一个函数,又被称为散列函数,可以大幅提升检索数据的效率。

  • Hash 算法是通过某种特定性的算法(比如:MD5、SHA1、SHA2、SHA3)将输入转变为输出。相同的输入永远可以得到相同的输出,假设输入内容有微小偏差,在输出中通常会有不同的结果。

  • Hash值的长度是固定的,而输入数据的可能性是无限的,这就会出现不同的输入数据映射到了相同的Hash值,这种现象就叫Hash碰撞

  • 解决Hash碰撞的常见方案有开放寻址法和链表法。开放寻址法是在发生碰撞时,尝试寻找下一个可用的槽位来存储数据。链表法则是将映射到同一个Hash值的数据存储在一个链表中,通过链表来解决碰撞问题

  • 当链表长度过长时,查询效率会降低,此时可以将链表转换为红黑树来提高查询效率。红黑树是一种自平衡的二叉查找树,能够保证最坏情况下的查询时间复杂度为O(log n),其中n为节点数量。

从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第1张图片
举例:如果想要验证两个文件是否相同,可以直接对比两个文件通过Hash函数计算出的结果,如两个文件通过Hash函数计算的结果一样,即文件相同,否则不同。

 
提高查询速度的数据结构,常见的有两类:

  • 树,例如:平衡二叉树,查询、插入、修改、删除的平均时间复杂度都是O(log2N) .
  • 哈希,例如:HashMap,查询、插入、修改、删除的平均时间复杂度都是O(1) ;(key,value)

采用 Hash 进行检索效率非常高,基本都是一次检索就能找到数据,B+Tree 需要自顶向下依次查找,多次访问节点才能找到数据,中间需要多次 I/O 操作,从效率上来将 Hash 比 B+Tree 更快

Hash 索引适用的存储引擎如表所示:

索引/存储引擎 InnoDB MyISAM Memory
Hash 索引 不支持 不支持 支持

Hash 索引的使用场景:

  • ① 当字段的重复度低,且经常需要进行等值查询时,采用 Hash 索引是个不错的选择。

  • ② Redis 存储的核心就是 Hash 表。

  • ③ InnoDB 提供自适应 Hash 索引 (Adaptive Hash Index)
     

1.2、InnoDB索引为啥不选 Hash 结构

 
Hash结构效率高,那为啥InnoDB的索引结构要设计成树型呢?

  • 原因①,Hash索引仅能满足 (=)(<>)和 in 查询。如果进行 范围查询,时间复杂度会退化为O(n),树型结构的有序特性,依然能保持 O(log2N) 的高效率。
  • 原因②,Hash索引有一个缺陷,数据的存储是没有顺序的,在 ORDER BY 的情况下,使用 Hash 索引还需要对数据重新排序。
  • 原因③,对于联合索引而言,Hash值是将联合索引键合并后一起来计算,无法对单独的一个键或几个索引键进行查询。
  • 原因④,对于等值查询而言,通常 Hash 索引的效率更高,但如果索引列的重复值很多,效率就会降低。因为当遇到 Hash 冲突时,需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时。故 Hash 索引常不会用到重复值多的列上,比如 性别、年龄等。

 

1.3、关于InnoDB 提供自适应 Hash 索引 (Adaptive Hash Index)

 
       如果某个数据经常被访问,当满足一定条件时,会将这个数据页的地址存放到 Hash 表中,下次查询的时候,就会直接找到这个数据页所在的位置。采用自适应 Hash 索引的目的就是,方便根据 SQL 的查询条件,加速定位到叶子节点,特别是当 B+Tree 比较深,层级比较多时,自适应Hash能显著提高数据的检索效率。

 

-- 查询是否开启 自适应Hash索引,默认为 ON ,开启.
show variables like '%adaptive_hash_index%';

从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第2张图片

 

2、二叉搜索树

 
如果我们利用二叉树作为索引结构,那么磁盘的IO次数和索引树的高度是相关的。

二叉搜索树(二叉查找树)的特点

  • 一个节点只能有两个子节点,也就是一个节点度不能超过2.
  • 左子节点 < 本节点;右子节点 >= 本节点,比我大的向右,比我小的向左

 

二叉搜索树的查找规则:

二叉搜索树(Binary Search Tree),搜索某个节点和插入节点的规则一样,假设搜索插入的数值为key:

  • ① 如果 key 大于根节点,则在右子树中进行查找;
  • ② 如果 key 小于根节点,则在左子树中进行查找;
  • ③ 如果 key 等于根节点,也就是找到了这个节点,返回根节点即可。

从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第3张图片
二叉查找树可能退化成一条链表,查找数据的时间复杂度为O(N),如下图:
从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第4张图片

 

3、平衡二叉树(AVL树 )

 
       为解决二叉搜索树可能退化成链表的问题,就出现了平衡二叉树(Balanced Binary Tree),又称为 AVL 树(有别于 AVL 算法),它是在二叉搜索树的基础上增加了一些约束:平衡二叉树是一课空树,或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树

       常见的平衡二叉树包含:平衡二叉搜索树红黑树数堆伸展树一般平衡二叉树指的就是平衡二叉搜索树,搜索的时间复杂度为O(log2n),数据查询的时间主要依赖于磁盘的 I/O次数,当n 越大,查询越复杂。

从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第5张图片

       每访问一次节点就需要进行一次磁盘I/O操作,以上图示例来讲,需要进行4次I/O操作。虽然平衡二叉树的效率高,但是树的深度比较高时,也意味着磁盘I/O操作次数多,就会影响整体数据查询的效率。

 

4、B-Tree(B树)

 
       B树(Balance Tree)也就是多路平衡查找树,简写为 B-Tree(这里的横杠是连接符,而不是减号)。B-Tree 的高度远小于平衡二叉树的高度。B-Tree 关键点如下:

  • B-Tree 在插入、删除节点的时候,如果导致树不平衡,就通过自动调节节点的位置来保持树的自平衡

  • 关键字集合分布在整棵树中,如果一个磁块包括了x个关键字,那指针数为x+1,叶子节点和非叶子节点都存放数据。搜索有可能在非叶子节点结束。

  • 搜索性能等价于在关键字全集内做一次二分查找。

  • 对于一个M阶(每个节点最多可以包括M个子节点)的B-Tree,根节点的儿子数的范围是[2,M],且所有叶子节点位于同一层。

    从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第6张图片

假设查询关键字为 8 的数据。

  • ① 与根节点的关键字26 和 36 比较,8 小于26 ,取磁块1的P1指针向下级节点查找。
  • ② 按磁块1的P1指针找到磁块2,比对关键字 6 和 21,8大于6且小于21,取取磁块2的P2指针向下级节点查找。
  • ③ 按磁块2的P2指针找到磁块6,对比磁块6的关键字8和18,找到关键字8.

       在B-Tree 的搜索中,是把数据读取出来,然后在内存中进行比较,那比较的时间可忽略不计。读取磁盘块本身需要进行I/O操作,其消耗的时间比在内存中进行比较需要的时间较多,是数据查找用时的重要因素。B-Tree相比于平衡二叉树来说,磁盘I/O操作要少效率高,只要树的高度足够低,I/O次数足够少,就可以提高查询性能

 

5、B+Tree (B+树)

 

5.1、B+Tree 结构

 
       B+Tree 是一种多路搜素树,基于B-Tree做出了改进。主流的 DBMS都支持 B+Tree 的索引方式。B+Tree相比于B-Tree在查询效率、磁盘读写性能和范围查询方面更具优势,B+Tree 比 B-Tree 更适合文件索引系统
 
从 Hash索引、二叉树、B-Tree 与 B+Tree 对比看索引结构选择_第7张图片

B+Tree和B-Tree的区别主要体现在以下三个方面:

  • 内部节点存储数据的方式:B-Tree的每个内部节点都存储数据,而B+Tree的内部节点并不存储数据,只作为索引使用,数据都存储在叶子节点上。因为B+Tree只在叶子节点存储数据,所以在查询时稳定性更好,且查询速度更快。

  • 叶子节点的链接方式:B+Tree的叶子节点之间通过指针连接,形成一个有序链表,便于进行范围查询。而B-Tree的叶子节点并没有相连,B-Tree 做范围查询时,需要通过中序遍历才能实现。在范围查询上,B+Tree 的效率比 B-Tree 高。

  • 磁盘读写性能:B+Tree相比于B-Tree,在磁盘读写性能上更有优势。这是因为B+Tree的内部节点并没有指向关键字具体信息的指针,所以其内部节点相对B-Tree更小,如果把内部节点和叶子节点存放在同一块磁盘内,那么盘块容纳的节点数目比B-Tree更多,一次性读入内存中的需要查找的关键字也就越多,相对来说IO读写次数也就降低了。
     

5.2、B+Tree 相关思考题

 
1、为了减少IO,B+Tree 索引树会一次性加载吗?

  • ① 数据库索引存储在磁盘上,如果数据量过大,必然会导致索引的大小也会很大,超过几个G.
  • ② 当利用索引查询时,是不可能将全部几个G 的索引数据,全都加载到内存中的。只能逐一加载每个磁盘页,因此磁盘页对应着索引树的节点。

 
2、B+Tree 的存储能力如何?为何说一般查找行记录,最多只需要 1~3次磁盘IO ?

InnoDB 存储引擎中数据页的大小默认为 16KB,一般表的主键类型为 int(占4个字节)或 bigint(占8个字节),指针类型一般也为4或8个字节,所以一个根节点或内节点的数据页(B+Tree中的一个节点)中大概存储 16KB/(8B+8B) = 1K 个键值。估算一下,就当K 的取值为10^3,那一个深度为3 的B+Tree 索引可以维护的数据为 10^3 * 10^3 * 10^2 = 1亿 条记录(这里假设一个叶子节点的数据页可以存 100 条行记录数据)。

实际情况中每个节点可能不会填充满,所以在数据库中,B+Tree 的高度一般都在 2~4 层。MySQL 的 InnoDB 存储引擎在设计时,将根节点常驻内存,也就是说查找某一键值的行记录时,最多只需要1~3次磁盘 IO 操作。

 
3、为什么说B+Tree 比 B-Tree 更适合实际应用中,操作系统的文件索引和数据库索引?

  • B+Tree 的磁盘读写代价更低

B+Tree 的内部节点,并没有指向关键字具体信息的指针,故其内部节点相对B-Tree 更小。通俗来说,就是B-Tree每个层级的节点都存放的行数据,但是B+Tree 只有在叶子节点存放行数据,内节点存放主键或非聚簇索引字段,所以一般情况来讲相同的数据体量,B+Tree的层级会低,磁盘IO操作次数更少,磁盘读写代价更低。

  • B+Tree 的查询效率更稳定

B+Tree 的叶子节点才会有用户记录行数据,所以任何关键字的查找,必须走一条从根节点到叶子节点的路,所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

 
4、Hash 索引和 B+Tree 索引的区别?

  • ① Hash 索引不能进行范围查询,B+Tree 可以。

  • ② Hash 索引不支持联合索引的最左原则(即联合索引的部分索引无法使用),B+Tree 可以。

  • ③ Hash 索引不支持 ORDER BY 排序,B+Tree 可以(B+Tree索引数据是有序的)。

  • ④ Hash 索引不支持模糊查询,B+Tree 使用LIKE进行模糊查询时, LIKE 后面后模糊查询(比如%结尾)可以起到优化作用。

  • ⑤ InnoDB 不支持哈希索引,但提供自适应 Hash 索引 (Adaptive Hash Index)。

 

5、Hash 索引和 B+Tree 索引是在键索引时,手动指定的吗?

对于MySQL而言,针对 InnoDB 和 MyISAM 存储引擎,都会默认采用 B+Tree 索引,无法使用 Hash 索引。InnoDB 提供的自适应 Hash 是不需要手动指定的。如果 Memory/Heap 和 NDB 存储引擎,是可以进行选择 Hash 索引的。

 

 
 
 
 
 
 
 
.

你可能感兴趣的:(MySQL,哈希算法,二叉树,Hash,B+Tree,B树,B+树,B-Tree)