Mysql 上級学習まとめ 6: インデックスの概念と理解、B+ ツリー生成プロセスの詳細な説明、MyISAM と InnoDB の比較

1. インデックスの紹介

1.1 インデックスを使用する理由

以前に多くのSQL構文を学習しましたが、選択クエリステートメントを入力したことを理解する必要があります.mysqlはクエリステートメントに対応するデータをどのように見つけますか? また、どうやってすぐに見つけましたか?

ここで、インデックスの概念を紹介します。インデックスとは、ストレージ エンジンがデータ レコードをすばやく検索するために使用するデータ構造です

上記の概念が明確にわかるかどうかわかりませんが、インデックスはデータ構造です!

1.2 データレコードの検索

次に、例を通してゆっくりと理解するために、インデックスはデータ構造であり、正確にはどういう意味ですか...

たとえば、今回は従業員テーブルがありますが、select ステートメントを入力した後、このデータをどのように検索すればよいでしょうか。次に分析してみましょう。

select * from employees where employee_id=100;

ここに画像の説明を挿入

最初に、単純なテーブル index_demo テーブルを作成します。このテーブルには 3 つのフィールド (c1、c2、c3) しかなく、c1 フィールドは主キーです。

CREATE TABLE index_demo(
    c1 int,
    c2 int,
    c3 char(1),
) ROW_FORMAT = Compact;

ここで使用される行形式は Compact です。これは、データの各行が実際に保存される形式です

  1. record_type : レコードのタイプを示す、レコード ヘッダー情報の属性。0 は通常のレコード、1 はディレクトリ レコード、2 は最小レコード、3 は最大レコードを意味します
  2. next_record : このレコードに関連する次のアドレスのアドレス オフセットを示す、レコード ヘッダー情報の属性。
  3. 各列の値: ここでは、index_demo の 3 つの列 (c1、c2、c3) のみが記録されています。
  4. その他の情報:上記3種類の情報を除くすべての情報で、その他の非表示の列の値や記録された追加情報を含みます。

ここに画像の説明を挿入

1.3 データ挿入、複数ページ検索

テーブルを作成したら、データの挿入と検索を開始します。最初に 3 つのデータを挿入します。

INSERT INTO index_demo VALUES 
(1, 4, 'u'),
(3, 9, 'd'),
(5, 3, 'y');

Mysql はディスク内のデータ レコードをページ単位でロードします。1 ページのサイズは 16KB であるため、1 回のロードで最大 16KB のデータをロードできます。

3 つのデータのみを挿入しましたが、3 つのデータが 16KB に達したと仮定すると、以下に示すように 1 ページを埋めることができます。
ここに画像の説明を挿入

このとき、主キー(c1)の大きさに応じて、データが1ページ内の一方向連結リストに連結されていることがわかる。データの一部を見つけたい場合は、このページで順番に検索できます。

これらの 3 つのデータで 1 ページが既にいっぱいになっているため、この時点で別のデータが挿入されると、1 ページのアドレスが新しいデータ レコードを格納するために再割り当てされます。格納後、主キーでソートするので、レコードを移動する必要があるかどうかを確認する必要があります.レコードを移動する必要がある場合は、主キーのサイズに応じてレコードを移動する必要があります.このプロセスは呼び出されます.ページング

たとえば、この時点で別のデータが挿入されます。次に、主キーの値が 5 のレコードを新しく割り当てられた 28 ページに移動し、主キーの値が 4 のレコードを 10 ページに挿入する必要があります。

INSERT INTO index_demo VALUES 
(4, 4, 'a');

ここに画像の説明を挿入


ここに画像の説明を挿入
さて、データベース レコードの増加に伴い、下図に大まかに示すように、ますます多くのページがデータベースに新たに割り当てられます。接続が使用されます。
2) 各ページについては、単方向リンク リストのクエリ速度が遅いため、各データのアドレスを記録する配列を維持できます。クエリが必要な場合は、バイナリ検索を使用して、このページでこのデータをより速く見つけることができます。

したがって、(20,2,'e') このデータを見つけたい場合。
1) 最初に 10 ページを見つけて、2 点検索で見つからない
2) 次にリンク リストで次の 28 ページを見つけ、二分検索でまだ見つからない
3 ) 次に、リンクされたリストを介して次のページ 9 を見つけます。次に、バイナリ検索を介して、この時点で見つけることができます。これは、データ レコードを返すことです。

1.4 ディレクトリ エントリ レコードに基づくページ ルックアップ

1.3 では、複数ページの検索に明らかな問題があることがわかります。つまり、データ量が増加した後、1 つずつ順番に検索する速度が遅すぎます。

したがって、ディレクトリ項目はページごとに作成でき、各ディレクトリ項目には次の 2 つの部分が含まれます。

  1. key で表される、ページの最小主キー値
  2. page_no で表されるページ番号

したがって、1.3 での複数のページは、現時点では次の図で表すことができます。
この時点では、まだ (20,2,'e') このデータを探しています。
1) このカタログアイテムのページで、カタログアイテム 3 を 2 ポイント検索で見つけます。主キー 20 は、ディレクトリ アイテム 3 の最小主キー 12、Xiaoyu ディレクトリ アイテム 4 の最小主キー 209 よりも大きいためです。
3) ディレクトリ項目 3 の 9 ページに移動し、2 点検索でこのレコードを見つけます。

この時点で、1.3 では検索がページごとに実行され、毎回ディスクから 1 ページのデータをロードする必要があることがわかります。これには多くの時間がかかります。ディレクトリを追加した後、データを見つけるためにディスクのページを 2 回ロードするだけで済みます。

ディスク ページの読み込みにかかる時間は、インメモリの読み込みにかかる時間よりもはるかに長く、2 つの値の大きさは少なくとも 10 以上であることに注意してくださいしたがって、この時点では、プログラム内のアルゴリズムの時間計算量が 0(n) か O(n2) かを気にする必要はありません。ディスクがページを何度もロードすると、メモリ内でプログラムを実行する時間よりもはるかに時間がかかるためです。
ここに画像の説明を挿入

このとき、ディレクトリエントリレコードに基づくページのデータ構造は下図のようになります。
ここに画像の説明を挿入
第 1 層はディレクトリ エントリ レコードであり、第 2 層はデータ レコードであることがわかります。
ディレクトリ エントリには、主キーの最小値と、対応するページの物理アドレスのみが記録されます。データ レコードには、実際にはこのレコードのデータが含まれています。それらの区別は、前に紹介した record_type 属性に基づいています。

  • 0: 共通ユーザー レコード
  • 1: ディレクトリ エントリ レコード
  • 2: 最低限の記録
  • 3: 最大記録

データ レコードにはデータが含まれているため、1 ページ (16KB) に格納されるディレクトリ レコードの数は、多くの場合、既存のデータ レコードの数よりも多いことに注意してください。

たとえば、データ レコードのサイズが 160B の場合、ディスクのページには 100 個のデータ レコードを格納できます。
ディレクトリ レコードにはページの最小主キー値とページの物理アドレスしかないため、ディレクトリ レコードのサイズが 16B であると仮定すると、値は 2 つしかなく、ディスクのページには 1000 レコードを格納できます。
したがって、この時点で 2 レベルのディレクトリ構造に格納できるデータ レコードの数は、1000 * 100 = 100,000、つまり 100,000 レコードです。

したがって、100,000 レコードの場合、最初のレイヤーで 2 点検索を使用してページの物理アドレスをすばやく見つけ、次にこのページでバイナリ検索を使用してこのデータをすばやく見つけることができます。したがって、ディスク ページを約 2 回ロードすることで、100,000 個のデータを見つけることができます。

1.5 カタログ アイテム レコード ページに基づくカタログ ページ

上記の例で、100,000 個を超えるデータがある場合はどうなるでしょうか。その 1 つのディレクトリ エントリ レコードでは、絶対に十分ではありません。たとえば、1 億個のデータがある場合、上記の例によると、1000 個のディレクトリ アイテム レコード ページが必要です。
ここに画像の説明を挿入

このとき、特定のデータを検索したい場合は、最初のレベルのディレクトリ エントリ レコード ページで 1 つずつ検索する必要があり、そのたびにディスク ページをロードする必要があるため、速度が非常に遅くなります。

したがって、上記の方法を参照して、別のレイヤーを追加することができます: カタログ エントリ レコード ページのカタログ ページ。以下に示すように:
ここに画像の説明を挿入

検索方法は先ほどと同様で、さらにレイヤーが追加されます.上記の例では、このときに格納できるデータの数は、1000 (第 1 レベルのディレクトリ項目ページ) × 1000
( 2 番目のレベルのディレクトリ アイテム ページ) * 100 = 1,0000,0000 個のデータ、つまり 1 億個のデータ。

もちろん、データの量が多い場合は、レイヤーの数を増やし続けることができます。もう1層追加すれば、1000億個のデータを格納できますが、これは一般的なビジネスではすでに多いため、一般的なインデックスの層数は4層を超えることはありません.

1.6 B+ ツリー

上記は、データベース内のデータ レコードをすばやく検索するためのデータ構造の作成方法を分析したもので、このデータ構造は大まかに次の図のようになります: このデータ構造の名前は B
ここに画像の説明を挿入
+ ツリーです。

ユーザー レコードを格納するデータ ページであれ、ディレクトリ アイテム レコードを格納するデータ ページであれ、それらは B+ ツリーのデータ構造に格納されるため、これらのデータ ページ ノードとも呼ばれます。図からわかるように、実際のユーザー レコードは実際には B+ ツリーの一番下のノードに格納されます。これらのノードはリーフ ノードとも呼ばれ、ディレクトリ アイテムの格納に使用される残りのノードは非リーフ ノードまたは非リーフ ノードと呼ばれます。内部ノード B+ ツリーの最上部にあるノードは、ルート ノードとも呼ばれます。

通常、使用する B+ ツリーは 4 層を超えません。
上記の例は示されていますが、ここに要約があります。
データ レコードのサイズが 160B であると仮定すると、ディスク ページ (16K) には最大 100 個のデータを格納できます。ディレクトリ ページは、データ レコードの最小主キー値とデータ レコード ページのアドレスのみを格納する必要があるため、ディスク ページに格納されるディレクトリ エントリ データは、1000 エントリが可能であると仮定すると、データ項目の数より多くなければなりません。保管されます。

  1. B+ ツリーにレイヤーが 1 つしかない場合: ディスク ページ (16K) には最大 100 個のデータを格納できます。
  2. B+ 木が 2 層の場合: 1000 × 100 = 100,000 (100,000 個のデータ) まで格納できます。
  3. B+ 木が 3 層の場合: 1000 × 1000 × 100 = 1,0000,0000 (1 億個のデータ) まで格納できます。
  4. B+ 木が 4 層の場合: 1000 × 1000 × 1000 × 100 = 1000,0000,0000 (1000 億個のデータ) まで格納できます。

したがって、1,000 億個のデータの場合、主キーの値からデータを検索するために、最大 4 つのディスク ページ (3 つのディレクトリ アイテム ページ、1 つのユーザー データ レコード ページ) をロードするだけで済み、ページ ディレクトリ (ページつまり、リンクされたリストを介して 1 つずつクエリを実行することなく、二分法を使用してすばやく見つけることができます。

2. インデックスの概要

最初のセクションまで、mysql でのデータ レコードの B+ ツリー検索のプロセス全体を分析しましたが、この時点で、インデックスの概念と長所と短所を理解することをお勧めします。そうしないと、多くのテキストの説明を見たときに、非常に混乱する可能性があります。

では、なぜインデックスを作成する必要があるのか​​を説明する必要があります。上記から、インデックス作成の目的は、ディスク I/0 の数を減らし、クエリの効率を向上させることで
あることがわかります。

2.1 インデックスの概要

インデックスは、mysql のカレッジや大学がデータを取得するのに役立つデータ構造であるため、インデックスはデータ構造です

インデックスはストレージ エンジンに実装されるため、各ストレージ エンジンのインデックスは必ずしも同一ではなく、各ストレージ エンジンが必ずしもすべてのインデックス タイプをサポートしているわけではありません。

同時に、ストレージ エンジンは、各テーブルのインデックスの最大数とインデックスの最大長を定義できます。すべてのストレージ エンジンは、テーブルごとに少なくとも 16 個のインデックスをサポートし、インデックスの合計の長さは少なくとも 256 バイトです。一部のストレージ エンジンは、より多くのインデックスとより大きなインデックス長をサポートします。

2.2 索引の利点

  1. 大学図書館が構築する書誌索引と同様に、索引を作成する主な理由は、データ検索の効率を向上させ、データベースの IO コストを削減することです。
  2. 一意のインデックスを作成することにより、データベース テーブル内のデータの各行の一意性を保証できます。
  3. データの参照整合性を実現するという点では、テーブル間の結合を高速化できます。つまり、依存する子テーブルと親テーブルを組み合わせてクエリを実行すると、クエリの速度を向上させることができます。
  4. データ クエリにグループ化句と並べ替え句を使用すると、クエリでのグループ化と並べ替えの時間を大幅に短縮でき、CPU 消費量を削減できます。

2.3 索引の欠点

  1. インデックスの作成やメンテナンスに時間がかかり、データ量が増えるほどかかる時間も増えます。
  2. インデックスはディスク スペースを占有する必要があります。データ テーブルが占有するデータ スペースに加えて、各インデックスも一定量の物理スペースを占有し、ディスクに格納されます。多数のインデックスがある場合、インデックス ファイルはデータ ファイルよりも早く最大ファイル サイズに達する可能性があります。
  3. インデックスによってクエリ速度は大幅に向上しますが、テーブルの更新速度は遅くなります。テーブル内のデータを追加、削除、および変更する場合、インデックスも動的に維持する必要があるため、データの維持の速度が低下します。

3. 索引付けの一般的な概念

インデックスの物理的な実装に応じて、クラスター化インデックスと非クラスター化インデックスの 2 つのタイプに分けることができます。非クラスター化インデックスは、セカンダリ インデックスまたは補助インデックスとも呼ばれます。

3.1 クラスタ化インデックス

クラスタ化インデックスは個別のインデックス タイプではなく、データ ストレージ メソッド (すべてのユーザー レコードがリーフ ノードに格納される) です。つまり、いわゆるインデックスはデータであり、データは index です

この種のクラスター化インデックスでは、INDEX ステートメントを明示的に使用して mysql で作成する必要はありません. InnoDB ストレージ エンジンは、クラスター化インデックスを自動的に作成します.

利点:

  • データ アクセスが高速になりますクラスター化インデックスはインデックスとデータを同じ B+ ツリーに格納するため、クラスター化インデックスからのデータのフェッチは、非クラスター化インデックスよりも高速です。
  • クラスター化インデックスは、主キーの並べ替え検索と範囲検索で非常に高速です。
  • クラスター化インデックスの順序に従って、クエリが特定の範囲のデータを表示する場合、データは密接に接続されているため、データベースは複数のデータ ブロックからデータを抽出する必要がなく、インデックスによって多くの io 操作が節約されます

短所:

  • 挿入速度は挿入順序に大きく依存します. 主キーの順序で挿入するのが最速の方法です. そうしないとページ分割が発生し、パフォーマンスに深刻な影響を与えます. したがって、InnoDB テーブルの場合、自動インクリメント ID 列は通常、主キーとして定義されます
  • 更新中の行が移動されるため、主キーの更新にはコストがかかります。したがって、innoDB テーブルの場合、通常は主キーを更新不可として定義します。
  • セカンダリ インデックス アクセスには、2 つのインデックス ルックアップが必要です1 回目は主キー値を検索し、2 回目は主キー値に基づいて行データを検索します。

制限事項

  • mysql データベースの場合、現在 innodb データ エンジンのみがクラスター化インデックスをサポートしていますが、myisam はクラスター化インデックスをサポートしていません。
  • データの物理的な格納方法は 1 つしかないため、各 mysqlテーブルにはクラスター化インデックスを 1 つだけ持つことができます通常、これはテーブルの主キーです。
  • 主キーが定義されていない場合、 InnoDB は代わりに空でない一意のインデックスを選択しますそのようなインデックスがない場合、InnoDB は主キーを clustered index として暗黙的に定義します
  • クラスター化インデックスのクラスター化特性を最大限に活用するために、innodb テーブルの主キー列は、順序付けられたシーケンス ID を可能な限り使用する必要があり、UUID、MD5 などの順序付けられていない ID を使用することはお勧めしません。 、HASH、および主キーとしての文字列列は、データの順序の増加を保証できません。

3.1 非クラスタ化インデックス (セカンダリ インデックス、補助インデックス)

上記で紹介したクラスター化インデックスは、検索条件が主キーの場合にのみ機能します。これは、B+ ツリーのデータが主キーに従って並べ替えられるためです。では、他の列を検索条件として使用したい場合はどうすればよいでしょうか?

さらにいくつかの B+ ツリーを構築できます
異なる B+ ツリーのデータは、異なる並べ替え規則を採用します. たとえば、上記の例の列 c2 のサイズは、別の B+ ツリーを構築するためのデータ ページとして使用できます。
ここに画像の説明を挿入
テーブルに戻るという概念:
c2 列のサイズでソートされた B+ ツリーによると、検索したいレコードの主キー値しか決定できないため、完全なユーザー レコードを検索したい場合は、まだクラスター化されたインデックスでもう一度チェックする必要があります. このプロセスはバックテーブルを呼び出しました.

非主キー列に従って構築されたこの種の B+ ツリーは、完全なユーザー レコードを見つけるためにテーブルを返す操作を必要とするため、この種の B+ ツリーはセカンダリ インデックス (セカンダリ インデックス) または補助インデックスとも呼ばれます。

非クラスター化インデックスが存在しても、データのサブクラスター化インデックスの編成には影響しないため、1 つのテーブルに複数の非クラスター化インデックスを含めることができます。

まとめ:

  1. クラスター化インデックスのリーフ ノードにはユーザー データ レコードが格納され、非クラスター化インデックスのリーフ ノードにはデータの場所が格納されます。非クラスター化インデックスは、データ テーブルの物理的な格納順序には影響しません。
  2. 並べ替えと格納の方法は1 つしかないため、1 つのテーブルには 1 つのクラスター化インデックスしかありませんが、複数の非クラスター化インデックスが存在する可能性があります。つまり、複数のインデックス ディレクトリがデータ検索を提供します。
  3. クラスター化インデックスを使用すると、データのクエリ効率は高くなりますが、データの挿入、削除、更新などを行うと、非クラスター化インデックスよりも効率が低下します。

3.3 関節指数

結合インデックスは、同時に複数の列にインデックスを付けるという点を除いて、非クラスター化インデックスの一種と見なすことができます。

たとえば、上で紹介した c2 列と c3 列を使用して、インデックスを作成します。
ここに画像の説明を挿入

4. InnoDB の B+ ツリー インデックスに関する注意事項

4.1 ルートページの場所は一万年変わらない

B+ ツリー インデックスのルート ノードは、その誕生以来移動しません。つまり、テーブルの B+ ツリー インデックスが作成されるたびに、最初にユーザー レコードを格納するルート ノード ページが作成されページがいっぱいになると、ユーザー データがレイヤー 2 に到着するようにページ分割が発生します。ルート ノード ページは、ディレクトリ エントリ レコード ページになります

より一般的な説明は、上で紹介した B+ ツリーは上から下へゆっくりと作成されるというものです。

4.2 内部ノードにおけるディレクトリエントリレコードの一意性

次の図に示すように、非リーフ ノード、つまり内部ノードのディレクトリ エントリ レコードが完全に一致している場合。
次に、新しいデータがあります: 0,1,'c', どのページに挿入すればよいかわかりません.
ここに画像の説明を挿入

このとき、B+ ツリーの同じ層にあるノードのディレクトリ エントリ レコードが、ページ番号のフィールドを除いて一意であることを確認する必要があります。このとき、主キーの値を追加できます。内部ノードのディレクトリ エントリ レコードは一意である必要があります。 :

  • 索引付けされた列の値
  • 主キー値
  • ページ番号

ここに画像の説明を挿入

4.3 ページには少なくとも 2 つのレコードが保存されます

InnoDB のデータ ページには少なくとも 2 つのレコードが格納されます。そうしないと、上で紹介した B+ ツリー構造スキームは無意味になります。

5. MyISAM のインデックス スキーム

5.1 MyISAM インデックスの原理

MyISAM エンジンはインデックス構造として B+ ツリーを使用しますが、そのリーフ ノードのデータ フィールドにはデータ レコードのアドレスが格納されます

InnoDB のインデックスはデータ (.idb) です。つまり、クラスター化されたインデックスの B+ ツリーのリーフ ノードには、完全なユーザー データ レコードが含まれます。
MyISAM もツリー構造を使用しますがインデックスとデータを別々に格納します。

  1. MyISAM はテーブル内のレコードを、データ ファイル (.MYD)と呼ばれる別のファイルに挿入順に格納しますデータを挿入するときに主キーのサイズに従ってデータが意図的にソートされていないため、これらのデータを検索するために二分法を使用することはできません。
  2. MyISAM は、インデックス ファイル (.MYI)と呼ばれるファイルにインデックス情報を格納しますMyISAM はテーブルの主キーに対して個別にインデックスを作成しますが、インデックスのリーフ ノードに格納されるのは完全なユーザー レコードではなく、主キー値+ ユーザー データ レコード アドレスです

col1を主キーとしたインデックスファイルの格納形式を次の図に示します。
ここに画像の説明を挿入
次の図は、col2 で構築されたセカンダリ インデックスです。
ここに画像の説明を挿入

5.2 MyISAM と InnoDB の比較

MyISAM のインデックス作成方法はすべて非クラスター化です。InnoDB には、非クラスター化に加えて、クラスター化インデックスも含まれています。

  1. InnoDB のデータ ファイルは、それ自体がインデックス ファイル (.idb) ですMyISAM のインデックス ファイル (.MYI) とデータ ファイル (.MYD)は分離されており、インデックス ファイルにはデータ レコードのアドレスのみが保存されます
  2. InnoDB が主キー値に基づいてクラスター化インデックスを検索する場合、ユーザー データ レコードを 1 回検索するだけで済みます。ただし、MyISAM インデックス ファイルにはユーザー データ レコードのアドレスが格納されているため、テーブルを返す操作が必要です
  3. InnoDB の非クラスター化インデックスは、データ レコードの主キー値を格納し、テーブルに戻って主キー値からデータ レコードを見つける必要があります。MyISAM インデックスはユーザー レコードのアドレスを記録するため、MyISAM のリターン テーブル操作は InnoDB よりも確実に高速です
  4. InnoDB requires that the table must have a primary key. 明示的に指定されていない場合、null 以外の列が自動的に選択され、データ レコードが主キーとして一意に識別されます。主キーとして暗黙的なフィールドを生成します. このフィールドの長さは 6 バイトであり, タイプは長整数です. MyISAM はできません。

概要:
さまざまなストレージ エンジンのインデックス実装方法を理解することは、インデックスの正しい使用と最適化に非常に役立ちます。
例 1: InnoDB のインデックスの実装を理解すれば、長すぎるフィールドを主キーとして使用することが推奨されない理由を簡単に理解できます。すべての副次索引は主キー索引を参照するため、主キーが長いと副次索引が大きくなりすぎる可能性があります。
例 2: InnoDB では、単調でないフィールドを主キーとして使用することはお勧めできません。非単調な主キーを使用すると、新しいレコードを挿入するときに B+ ツリーの特性を維持するために、データ ファイルが頻繁に分割および調整されるため、非常に非効率的です. 自動インクリメントフィールドを主キーとして使用することをお勧めします. .

おすすめ

転載: blog.csdn.net/xueping_wu/article/details/125351669