索引是帮助 MySQL 高效获取数据的,已排好序的一种数据结构。一般采用的数据结构有:
在通常情况下,若要在 7 条记录中查询某条记录,按顺序表的查询效率较低。例如,查找 7 条记录中的 id 为 5 的值,按顺序查询要查询 5 次。若以二叉排序树来作为索引的数据结构,如下例:
图1. 二叉排序树作为索引数据结构的查询
id 为索引,此时仅需要查找 3 次。可见索引在优化查询方面的巨大价值。
但是以二叉排序树作为检索数据结构在也有诸多缺点。例如:
在插入新值后二叉树需要重新平衡才能保证索引的作用
二叉排序树若不进行平衡,则不能起到优化查询的目的。若要进行平衡,则会产生格外的性能开销。
例如,连续插入 id 值递增的数据,二叉树若不平衡,则和链表无异,查询效率也和顺序查询相同。
图2. 二叉排序树索引的连续递增插入
提示
若不清楚为什么新插入的值都链接到右侧,可以去回顾数据结构二叉排序树。
不适合做范围查找
如果在图1. 中情况做范围查询 id 大于 2 的值,查找过程会相对复杂,开销大。
随着数据量的增大,二叉树高度越来越高,查找效率下降
在第一点中提到必须平衡二叉树后才能起优化查询的作用。假设二叉树每次插入后都进行平衡,当数据量达到上十万时,二叉树的高度也会变得非常大,此时无论是平衡二叉树的操作,还是查找二叉树的操作开销都会非常大。
在上一节中介绍了索引如何提高查询效率。从中可见,选取合适的数据结构至关重要。我们继续看两种可用的数据结构 —— 红黑树 与 B-Tree。
红黑树是一种特殊 AVL 树(平衡二叉树),也称平衡二叉 B 树。其操作的插入与删除都会使得二叉树保持平衡,从而避免图2. 中的情况。
图3. 红黑树索引的连续等增插入
此时相比按顺序查找 5 次找到索引值为 5,红黑树只需要查找 3 次。但是红黑树依然没有解决范围查找与海量数据效率低下的问题。
B-Tree 又称多路搜索树,其插入与删除都可以保证平衡,特别的,B-Tree 是 “多叉” 的,而不是二叉的。
为什么想到 B-Tree 树呢?正是因为它是 “多叉” 的。这意味相比二叉树,其一个节点可以链接的节点多于 2(节点的度大于 2),这样节点数相同的情况下,B-Tree 的高度更小,查找的次数也更小。
图4. 高度为 3 的B-Tree(图中的 p 为指向下一节点的指针)
如图4. 所示,这是一颗高度为 3 ,每个节点可以容纳 3 个值的 B-Tree,一共容纳 29 个值。相比高度为 3 的满二叉树只能容纳 7 个值。
此时若假设,数据库表中有 29 条记录,以 id 为索引值分别建二叉树(考虑最理想情况下为平衡二叉树)和 B-Tree 的索引,并查找索引值 28。二叉树的高度为 5,则查找 28 需要进行 5 次查找,而 B-Tree 如图4. 高度为 3,查找 28 仅需要进行 3 次查找。
可见,B-Tree 相对容量大,相对高度增长慢,极大的改善了随着数据量的增大,二叉树高度越来越高,查找效率下降的问题。并且其插入删除也是可保持平衡的。现在唯一的问题就是无法支持范围查询。
提示
B+Tree 是应对数据库表索引的完美数据结构。其在保证 B-Tree 的基础上进行了改造,能够支持范围查询。
图7. B+Tree
如图7. 所示,B+Tree 有以下特征:
接下来我们对如图7. 的 B-Tree 树进行查询。
至此,B+Tree 解决了在第一节中提出的,二叉树作为索引数据结构的所有问题。
接下来我们理解索引情况下具体的 SQL 查询过程。
首先,我们要清楚,数据表文件是存储在磁盘中的,而不是内存中。并且不能说我们直接将磁盘中所有的数据全一次性加载入内存,这样不仅耗费内存而且加载时间很长。
而对于索引本身,也是存储在磁盘中的。索引所占的大小也可以很大,不能一次性加载入内存。数据库开始只加载索引的根节点入内存。
假设索引数据结构是 B-Tree(如下图8.),我们来进行一次查询。
图8. B-Tree 索引下的数据表存储
select * from index_id = 9
可见进行了 2 次(不包括根节点加载)磁盘读取就得到了查询的记录。
但是毕竟是 B-Tree,不支持范围查询,所以我们接下来继续看 B+Tree 的查询过程。
图9. B+Tree 索引下的 SQL
select * from index_id = 3
注意
B+Tree 为索引数据结构的情况下与 B-Tree不同。
B-Tree 中每个节点都保存了对应的磁盘地址,而 B+Tree 中只有叶节点才保存了磁盘地址数据。
所以即便在非叶节点找到了对应的索引值,也必须向下加载节点,直至叶节点,再获取磁盘地址数据。
select * from index_id > 4
可见对于如图9. 中的 B+Tree,1 - 8 索引值的查找全部都需要加载 3 次(不包括根节点加载)磁盘才能完成查询。
那这不是比 B-Tree 查找效率低吗?
实际上不是,图9. 是为了方便描述而设计的 B+Tree,其已经类似于二叉树,因为每个节点的度为 2;并且每个节点也只保存了两个索引值。但实际情况下,我们可以使得每个节点拥有多的分支,保存更多的索引值,如图10. 。此时对于上万条记录的索引存入其中,读取磁盘也仅需 3 次。
图10. B+Tree 索引通常情况
MySQL 两种常用的数据库引擎 MyISAM 和 InnoDB 都是使用 B+Tree 为索引结构的。但其不同在于,MyISAM 是非聚簇索引,而 InnoDB 是聚簇索引。
我们从 MyISAM 引擎的数据表文件与 InnoDB 引擎的数据表文件进行比较。
图11. MyISAM 引擎的数据表文件与 InnoDB 引擎的数据表文件
MyISAM 的数据表有三个文件,后缀分别为 .frm
、.MYD
、.MYI
。而 InnoDB 的数据表有两个文件,后缀分别为 .frm
、.ibd
。这里 .frm
文件不做讨论。
在本文中,图1. 、图8. 、图9. 展示的都是非聚簇索引的情况。以图9. 为例。
图12. MyISAM 文件情况
在 MYI 文件中,存储的其实就是索引结构 B+Tree,而数据表数据则都存储在 MYD 文件中。
所以在 MyISAM 的数据表进行检索查询时,先得到 MYI 文件中查,再到 MYD 中拿。这就是非聚簇索引。
InnoDB 的数据表文件除了 .frm
只有一个 .ibd
文件。这个文件相当于将 MyISAM 的两个文件结合了。如图13. 在索引值下,直接接上了数据表的记录。
图13. InnoDB 文件情况
这样的情况下,相比 MyISAM 两个文件分开,要先查再拿。InnoDB 聚簇索引在查找到之后就可以直接拿到数据记录,效率更高。
MyISAM 与 InnoDB 的其他不同
- MyISAM 在 MySQL 5.5 之前是默认数据库引擎,之后 InnoDB 为默认的数据库引擎
- MyISAM 可以不设立主键,InnoDB 必须建立主键。
- MyISAM 不支持事务,InnoDB 支持事务。
- MyISAM必须依靠操作系统来管理读取与写入的缓存,而InnoDB则是有自己的读写缓存管理机制。
- InnoDB 支持外键,而 MyISAM 不支持。
MyISAM 和 InnoDB 的区别还有很多,这里只列举部分,读者可以多查找资料。
文章名称 | 更新时间 |
---|---|
数据库索引原理与索引数据结构 | 2021-05-23 |
数据库索引优化 | 准备中 |