从二叉树到平衡二叉树,再到B-树,直至B+树,让我们逐步了解数据库索引底层的工作原理!
二叉树是一种每个节点最多有两个子树的树结构。通常,这些子树被称为“左子树”和“右子树”。二叉树常用于实现二叉查找树和二叉堆。
二叉树具有以下特点: - 每个节点包含一个元素以及最多两个子树,其中0≤n≤2。左子树和右子树有固定的顺序,左子树的值小于父节点,右子树的值大于父节点。 - 假设我们有一组数[35, 27, 48, 12, 29, 38, 55],顺序插入到二叉树中,可能会导致树的不平衡,例如按照升序插入会导致树退化成线性链表,从而影响查找效率。
为了改善这种状况,平衡二叉树应运而生。
平衡二叉树是一种特殊的二叉树,它不仅满足二叉树的基本特性,还具有一个额外的特点:左右子树的高度差不超过1,并且左右子树自身也是平衡二叉树。
比如,按照[35, 27, 48, 12, 29, 38, 55]的顺序插入,形成的二叉树已经是平衡的。如果按照[12, 27, 29, 35, 38, 48, 55]的顺序插入,平衡二叉树则能保持平衡,避免形成线性链表,从而提高查找效率。
平衡二叉树的查找效率很高,类似于二分查找。假设树的高度为h,树的最大节点数为2^0 + 2^1 + ... + 2^(h-1)。对于100万数据的平衡二叉树,高度大约为20,查找最坏情况需要20次查找。
然而,平衡二叉树在磁盘操作中的性能较差,因为每次查找需要多次磁盘IO。于是,B-树应运而生。
B-树是一种多路搜索树,每个节点可以包含多个子节点。B-树的特点包括: - 每个节点最多包含m个子节点。 - 除了根节点和叶子节点外,每个节点至少包含m/2(向上取整)个子节点。 - 所有的叶子节点位于同一层。 - 每个节点包含k个元素(关键字),m/2≤k。 - 节点中的元素按顺序排列,左子节点的值小于或等于节点的元素,右子节点的值大于或等于节点的元素。
以[0, 1, 2, 3, 4, 5, 6, 7]的数组插入一个3阶B-树为例,可以看到B-树如何满足这些特性。在B-树中查找元素的过程类似于二叉树,但通过更少的节点访问次数,提高了查找效率。
B+树是在B-树基础上的进一步优化,更适合用于外存储索引结构。B+树的主要特点包括: - 所有的非叶子节点只存储关键字信息。 - 所有的数据都存储在叶子节点中。 - 所有的叶子节点都包含所有元素的信息,并且叶子节点间有链接指针。
B+树与B-树相比,所有数据都存储在叶子节点,查找效率更高,因为每次查找只需到达叶子节点。此外,B+树的叶子节点间有链接指针,便于范围查询。
在选择B-树和B+树时,需要考虑以下因素: - B-树的非叶子节点存储实际数据,查找效率较高,但磁盘I/O次数较多。 - B+树的所有数据都存储在叶子节点,查找效率高,但需要更多磁盘空间。
鉴于以上比较,大多数关系型数据库采用B+树作为数据存储结构。
在InnoDB存储引擎中,每个页默认大小为16KB,每次读取数据时都是读取4个页。假设我们有一个用户表,插入数据时,为了减少数据移动,通常是插入到当前行的后面或已删除行留下的空间。因此,页内的数据可能不是完全有序的,但通过指针构成单向有序链表。
随着数据增加,页分裂会产生新的页。InnoDB的页分裂过程包括复制旧页内容到新页,并将新数据插入新页。页分裂过程中,非叶子节点只存储索引,叶子节点存储数据,叶子节点间有链接指针。
数据插入后如何查找?首先,通过根节点查找叶子节点,再在叶子节点内查找具体数据。InnoDB中定位到页后,通过页目录(Page Directory)中的稀疏索引结构快速定位到目标记录。
聚集索引存储数据本身,而非聚集索引存储的是数据的聚集索引键。通过非聚集索引查找数据时,需要先找到聚集索引键,再查找具体数据。
InnoDB的数据在物理上按主键顺序存放,而MyISAM按插入顺序存放。MyISAM的叶子节点不存储数据,非聚集索引查找数据时可以直接找到数据地址,无需回表操作,因此查找效率较高。
索引优化建议主要包括:
- LIKE
的模糊查询以%
结尾会导致索引失效。
- 表上的索引数量不宜过多,一般不超过5个。
- 尽量使用覆盖索引。
- 避免在重复数据多的列上建索引。
这些优化建议有助于提高查询效率,减少不必要的磁盘I/O操作。