Skip to content
旧时的足迹
Go back

MySQL索引与B-Tree

编辑此页

在介绍 B-Tree 前先稍稍介绍两个东西

二叉查找树

二叉查找树的几个性质:

二叉查找树查找、插入的时间复杂度为 O(log n)

二叉查找树的具体查找,添加,删除操作这里就不展开了

二分查找

二分查找每次把搜索区域减少一半,时间复杂度为 O(log n)。

其优点是比较次数少,查找速度快,平均性能好;缺点是要求待查表为有序表,且插入删除困难。

大致的核心实现如下(当然你很容易将递归改为循环):

int a[100];
int search(int left, int right, int value)
{
  int middle = left + ((right - left) >> 1);

  if (value < a[middle])
  {
    right = middle - 1;
    return search(left, right, value);
  }
  else if (value > a[middle])
  {
    left = middle + 1;
    return search(left, right, value);
  }
  
  return middle;
}

折半查找方法适用于不经常变动而查找频繁的有序列表。

很简单的一个算法,但是据说很多人不能一次性正确的写出实现。
首先是要注意数组边界,其次上边计算中间值时没有使用 ‘(right + left) >> 1’, 是因为 当量级足够大时,’+’ 操作会造成溢出。

B-Tree

与二叉查找树、平衡二叉查找树、红黑树等二叉树形结构相比,B-Tree 是一种多路查找树。不像前者每个节点最多可以有两个孩子节点, B-Tree 可以有许多子女,而且B树也可以在O(logn)时间内,实现各种如插入,删除等集合操作。更大的分支因子可以降低树的高度从而降低 频繁磁盘I/O读写所造成的查询效率低下。许多数据库系统都会使用B树或者B树的各种变形结构。

B-Tree 又叫平衡多路查找树。 一棵m阶的B-Tree有如下性质:

下图就是一个简单的3阶 B-Tree:

3阶 B-Tree

对于它的查找其实也很简单,比如我们要查找31:那么首先根据算法发现 31>20 & 31<33,找到指向块[24,28]的指针;根据指针找到块[24,28], 根据算法发现 31>28,找到指向[29,31]的指针;根据指针找到块[29,31],最终找到31。

我们知道,在一个有序序列中进行插入操作简直是个灾难,我们不但要给插入对象制造空间,还要对后续对象进行移位。

接下来来看一下 B-Tree 的插入过程(B-Tree是从叶子节点进行插入的)

我们拿一颗5阶树做例子,这样每个节点最多有5个孩子,同理每个节点最多就只能有4个关键字:

5-B-Tree-1
5-B-Tree-2
5-B-Tree-3
5-B-Tree-4

那么删除操作:

5-B-Tree-del-1
5-B-Tree-del-2
5-B-Tree-del-3
5-B-Tree-del-4

B-Tree 的特点:

B+ -Tree

B+ -Tree是B-Tree的一个变种。那么它们有什么不一样呢(同为m阶):

MySQL普遍使用B+Tree实现其索引结构,在InnoDB引擎中数据文件本身就是索引文件,其树形结构的每个叶子节点都包含了完整的数据记录, 这棵树的主干是由数据表的主键组成的,因此InnoDB表数据文件本身就是主索引。这也是为什么建议主键使用自增张字段,因为如果我们的主键是不规则的, 那么在新增数据的时候由于key的出现不规则就可能导致频繁的分裂合并节点操作,这是非常低效的。

上面提到 B+ -Tree 的各叶子节点间是有指针顺序相连的,这极大的提高了区间查询的效率,我们在查询一个区间内的数据时,只需要先查到区间头数据然后 顺着指针向后遍历即可得到区间内所有数据。

另外如果我们在非主键字段上建立索引时所使用的结构与主键索引是基本一致的,只有一点是这时的叶子节点将不会存储完整的数据记录,而是存储相应记录主键的值。


编辑此页
Share this post on:

Previous Post
Greedy-And-Annealing
Next Post
浅谈MySQL联合索引