MySQL がインデックスの実装に B+ ツリーを使用する理由

MySQL がインデックスの実装に B+ ツリーを使用する理由

この記事では主に、Mysql がインデックスの実装に B+ ツリーを使用する理由について説明します。ここで説明する目標は、InnoDBMysql のストレージ エンジンです。想像してみてください。もしあなたが Mysql の開発者であれば、適切なデータ構造をどのように選択するでしょうか?

まずは実際の現場から

データ構造は特定の問題を解決するために作成されるため、ユーザーが Mysql を使用する場合、要件は何ですか? 最も単純な要件は簡単に考えることができます。

  1. ID または他の列値によるクエリの一致
  2. IDによる範囲クエリ

ユーザーは、クエリのパフォーマンスができるだけ高くなることを強く望んでいますが、テーブルの場合、テーブル全体のスキャンを実行せずに、インデックスを介してデータを直接クエリできれば、それは素晴らしいことです。

適切なデータ構造を選択する

現時点では、Mysql 開発者は、ユーザーのクエリ パフォーマンスの問題を解決するために、適切なデータ構造を選択し始めることになります。代替案としては、 、 、これら 3 つのデータ構造が考えHashられB TreeますB+ Tree

インデックスのデータ構造としてハッシュを使用する場合

ハッシュは O(1) のクエリの複雑さを提供できます。同様のselect * from t where id = 3等価マッチングの場合、パフォーマンスは非常に高く、比類のないものと言えます。しかし、範囲クエリの場合、ハッシュは少し限界があります。ハッシュには複雑性がありません。範囲クエリを実行するには O(1) を使用します。このため、ハッシュは基になるインデックスの実装には適していません。

B ツリーまたは B+ ツリーを使用する

そうなると、残された選択肢は B Tree と B+ Tree だけになります。この 2 つのデータ構造は似ているため、これら 2 つのデータ構造のどちらかを選択する場合は、どちらが優れたデータであるかをより明確に知るために、2 つを一緒に比較するのが最善です。構造。

まず最初に、B ツリーがデータをどのように保存するかを明確にする必要があります。

b tree structure.png

我们现在以聚簇索引来举例,图中的数字代表主键值,后面的*代表该位置是存放实际的行数据的.每个节点的左指针指向下一级的节点,并且左边指向的节点的主键值大小比上一级的小,右边指向的节点的主键值比上一级的大.B Tree的一个重要特点是在每一个节点都存储了完整的行数据.

B+ Tree存储数据的方式是这样的:

B+ Tree Structure.png

B+ Tree的重要特点是:

  1. 只有叶子节点才会存放整行数据,而非叶子节点只存储主键值,用于向下搜索

  2. 叶子节点冗余了所有的主键值,并存储行数据,并且每个节点之间用双向链表进行连接

在图中的12这个节点中,后面是指向24这个节点的,在图中被省略了.

在这里,我们再强调一下B Tree和B+ Tree的重要区别:

  1. B Tree的每个节点都存储行数据,而B+ Tree只有叶子节点存放行数据.
  2. B Tree因为每个节点都存储行数据,所以没有必要在非叶子节点再冗余任何数据.B+ Tree因为只有叶子节点存储行数据,所以需要在最后一层冗余所有的主键值,并存储行数据,且节点之间用链表进行连接.

理解了这两者的区别之后,我们来考虑一下针对实际场景,哪个数据结构才是更好的选择.首先,我们考虑一下等值查询,对于B Tree来说,从根节点的主键值开始进行比较,根据左小右大的特点,可以在某个层级定位到整行数据并返回.对于B+ Tree来说,也是从根节点开始进行比较,不过最终必须定位到叶子节点才能获取到需要的数据.所以在等值查询这个场景下,B Tree看起来比B+ Tree来得好.

那么考虑一下范围查询,比如B Tree来说,查询数据跟等值查询的模式差不多,只不过需要扫描到多个层级的节点.举个例子,如果在上图中寻找主键大于等于10且小于等于24的行数据.

  1. 首先从根节点12开始,12是满足条件的,所以获取它的行数据,12后面的同级节点24也符合要求,所以也获取它的行数据.
  2. 从12的左指针找到下一个节点,第一个节点是8,不符合要求,之后向后找到它的同级节点10,符合要求,后面没有其他节点了,结束.
  3. 节点12的右指针(节点24的左指针)没有指向任何数据,所以无需再找到下一个节点,所有可能的节点都查询过了,查询结束.

我们可以从这个过程中看到,范围查询需要从根节点出发,然后可能要找到它的下一级节点,直到找到所有符合的数据.

对于B+ Tree来说,寻找主键大于等于10且小于等于24的行数据的流程是这样的:

  1. 从根节点12向左找到下一级的10这个节点,从10的左指针找到10所在的叶子节点,因为叶子节点是链表结构,那么可以从这个叶子节点的指针一直往后定位到24这个节点,然后返回这中间的所有数据.

实际上数据最终都是存储到磁盘上的,对于Mysql来说,数据是以页为单位来存储数据,通常为4KB,在上面的图中,我们可以理解成每一个大的长方形框是一个页,而每个页里面存放了很多节点,对于B Tree来说,每个页的节点都存放整行数据,对于B+ Tree来说,非叶子页的节点只存放id,也被称为索引页,而叶子节点存放整行数据.对于页的读取,就涉及到IO操作,要知道IO读取数据的速度比从内存读取数据要慢得多,通常读取页的时间在10ms左右.

以范围查询为例,我们从IO的角度来概括一下B Tree和B+ Tree的区别.对于B Tree而言,读取根节点需要一次IO操作,加载出页之后,当前页的数据可能只有部分符合要求,然后根据页的指针再进行IO操作,找到另外的页,整个过程需要更多的IO操作,并且因为每次读取的页并不是所有数据都满足要求,所以这种方式被称为随机IO.那么对于B+ Tree而言,也需要从根节点向下查询,这其中也涉及到随机IO,但定位到需要的叶子节点后,读取页时只需要根据链表来定位到下一个页,每次读取的页大概率都是符合要求的数据,这种方式被称为顺序IO.所以在范围查询中,B Tree需要更多的IO操作,这样就需要耗费更多的时间.如果对随机IO和顺序IO不是很理解,文末有个参考资料可以去看一下.

所以整体上来看,B+ Tree是更好的选择.

参考资料

おすすめ

転載: juejin.im/post/7235052693386264636