インデックスとは: 検索を高速化するデータ構造
インデックスのメリットとデメリット
インデックスの利点:
1. インデックスが確立された後、データベース検索データの速度は (正しく使用されていれば) 直線的に上昇します。データ量が増えるほど、その速度はより顕著になります。
2. グループ化と並べ替えの際、インデックスを使用すると高速化できます。
3. ユニークなインデックスを確立することでデータの一意性が確保され、他の制限を加える必要がない(インデックスの確立と一意性の両方が保証される)
4. テーブルをクエリする場合、主キー フィールドと外部キー フィールドに基づいてインデックスを構築すると、明らかなパフォーマンスの向上がもたらされます。
欠点:
1. インデックス作成によりローカル ディスク ファイルが生成されるため、インデックス データを保存するために追加のスペースが必要となり、ディスク使用量が増加します。
2. データを書き込む場合はインデックス構造を追加で維持する必要があり、データを追加、削除、変更する場合はインデックスに対する追加の操作が必要です。
3. データ書き込み時にインデックスを維持するには追加の時間オーバーヘッドが必要となり、SQL
書き込み効率が低下し、パフォーマンスが低下します。
さまざまなインデックスのメリットとデメリット
主キーインデックス
まず第一に、長すぎるフィールドは主キー インデックスとして使用すべきではなく、主キー インデックス フィールドは一意である必要があります。
したがって、通常、これら 2 つの要件を満たすことができる自己インクリメント ID を主キー インデックスとして使用します。
しかし、主キー インデックスとして UUID を使用しないのはなぜでしょうか? これも要件を満たしています。
InnoDB ストレージ エンジンはインデックスを格納するときにデータ構造として B+ ツリーを使用し、B+ ツリーはバイナリ関数に基づいており、つまりノードが順序付けされているためです (詳細については、このブログの B+ ツリーを参照してください)。 UUID はランダムですつまり、インデックスを構築するときにランダムな挿入を実行する必要があり、ランダムな挿入により B+ ツリーの既存の形状が崩れる可能性がありますが、主キー インデックスのリーフ ノードにはすべてのデータが含まれます。モバイル ノードはデータを移動しますが、これがデータ移行のコストとなります。
しかし、自動インクリメントを使用すると
ID
この問題は発生せず、新しく挿入されたデータはすべて最後に配置されます。したがって、主キーは別の要件を追加します。
ジョイントインデックス
結合インデックスはインデックス カバレッジを達成し、セカンダリ インデックスの戻り操作を減らして検索速度を向上させたり、グループ化と並べ替えを高速化したりできます。
ただし、たとえば、abc フィールドに結合インデックスを作成します。
次に、3つのステートメントがあります
SELECT * FROM テーブル WHERE a = 1、b = 1、c = 1;
SELECT * FROM テーブル WHERE a = 1 および c = 1;
SELECT * FROM テーブル WHERE b = 1 および c = 1;
3 番目のステートメントのみジョイント インデックスを使用できません。
クエリ条件の左端の条件はインデックスの左端の条件と一致する必要があるため、結合インデックスを使用するには、クエリ条件の最初の条件がここで一致する必要があります。
また、ジョイント インデックスは、次のような範囲クエリ (>、<) に遭遇すると一致を停止します。
select * from t_user where age > 20 and reward = 100000;
age はジョイントインデックスを使用できますが、reward は使用できません
プレフィックスインデックス
プレフィックス インデックスは、このフィールドの最初の数文字を使用してインデックスを作成します。通常のインデックスと比較して、記憶領域を節約できます。データ量が十分に多い場合、節約される記憶領域はかなり大きくなります。
ただし、フィールドの完全な値はそのインデックス ノードに格納されないため、
MySQL
プレフィックス インデックスによる均等なグループ化と並べ替え作業を完了することは不可能でありORDER BY、GROUP BY
、カバレッジ スキャンなどの操作を完了することも不可能です。全文インデックス
MySQL 5.6 以降で導入された InnoDB フルテキスト インデックス
全文インデックス作成は、データベースに保存されている大きなテキスト内のあらゆるコンテンツ情報を検索する技術です。
Baidu で MYSQL を検索して検索結果を見ると、赤い部分がキーワードに該当する箇所です。
これはフルテキスト インデックスの効果で、検索対象のフィールドがいくつかのルートに分割され、フルテキストで取得されます。
ファジーマッチングの効果はファジーマッチングの効果と似ていると思いますか(大きなファジーマッチングのように見えます) ファジーマッチングは効果を得ることができますが、テーブルが大きくなりデータが増えると、そのパフォーマンスは大幅に低下します。そして全文インデックスの導入 この問題は完全に解決され、構文の代わりに全文インデックスを使用してあいまいクエリを実現でき、そのパフォーマンスは2 倍速く
like%
なります。like%
N
全文インデックス作成の欠点:
①全文インデックスは単語分割に基づいて実装されているため、フィールドに全文インデックスを作成した後、フィールドに対して単語
MySQL
分割処理が実行され、その単語分割結果も全文インデックスに格納されます。 、そのため、全文インデックスのファイルは非常に大きくなります。②全文インデックスはフィールド値ごとに単語分割を行うため、フィールド値を変更してから単語を分割するのに時間がかかるため、フィールドデータを変更した直後には全文インデックスが自動更新されません。ストアド プロシージャを作成し、それを呼び出す必要があります。フルテキスト インデックス内のデータを手動で更新します。
③上記の2点に加えて、全文インデックスの最大の欠点は、中国語に対応するほどフレンドリーではないことです 英語と同様、単語を記号やスペースで直接区切ることができますが、中国語の場合はどうなのでしょうか?一言で表現するのは広範で奥が深く、文章を正確に分割することは不可能であるため、中国語を検索する場合は全文インデックスの精度に若干の問題があります。
一意のインデックス
ユニークインデックスはデータの一意性を確保することができ、検索時に1つのデータのみを返し下を向かないのに対し、通常のインデックスは下を向いて条件を満たさないことを知るため、検索速度が遅くなります。高速になります (読み取りのため、実際にはそれほど高速ではありません。この動作では、一度に 1 データ ページをメモリに読み込みます。メモリ内のクエリ操作は非常に高速です。詳細は、共通インデックスと一意インデックスで確認できます)
挿入するとき
現在のデータ ページがメモリにロードされたとき
一意のインデックス: まず挿入位置を見つけて競合があるかどうかを確認し、競合がない場合は挿入します。
通常のインデックス: 直接挿入
CPUの動作も悪くない
しかし、現在のデータであれば、
これは機能しません。つまり、一意のインデックスは変更バッファを使用できないため、各挿入操作には I/O 操作が必要であり、I/O 操作には手間がかかることがわかっています。
ハッシュインデックス
(見てないけど、知らないよりは知っていた方がいいですよね)
ハッシュインデックス、つまり
Hash
データ構造型のインデックスですが、誰もがこれに触れる機会は少ないと推測され、結局のところ、B+
インデックスを作成する際にはデフォルトでツリー構造が使用されます。しかし、クエリ速度と比較すると、ハッシュ インデックスは間違いなくMySQL
リーダーとしてふさわしいものです。ハッシュ構造を使用したインデックスはインデックス フィールドの値をハッシュ テーブルの形式で格納するため、このフィールドに基づいてデータをクエリする場合、データを取得するために必要なハッシュ計算は 1 回だけです。しかし、ハッシュ構造の致命的な問題は、順序が狂っていることです。つまり、ハッシュ インデックスのフィールドに基づいて並べ替えやグループ化などが不可能であることです。
したがって、テーブル内でソート作業を行わないことが確実な場合は、インデックス データ構造としてハッシュ構造を適切に選択することができ、これにより予期せぬパフォーマンス上の利点がもたらされます。
インデックスの失敗
確立されたインデックスは役に立たず、テーブル全体が直接スキャンされるだけです。
左または左のあいまい一致
インデックス B+ ツリーは「インデックス値」に従って順番に格納され、プレフィックスに従ってのみ比較できます。左側のあいまい一致と左右のあいまい一致はプレフィックスが不明です
関数を使用する
値列にインデックスを構築し、クエリ時に WHERE LENGTH(name)=6 を使用したとします。この時点では、インデックスは使用されません。理由は非常に簡単です。値にインデックスを構築し、機能上ではありません。
しかし、MYSQL は実際には単なる関数インデックスであり、LENGTH(name) インデックスを作成すると、クエリ時にそのインデックスを使用できます。
式を使用する
ここでの失敗の理由は、関数を使用した場合と同じです (例: WHERE id + 1 = 10)。
ただし、WHERE id = 10 - 1 を使用すると、MYSQL は判断が遅いため使用できます。そのため、自分で変更できます。
暗黙的な型変換
たとえば、最初に値 VARCHAR(20) を定義しましたが、クエリ時に一重引用符を書き忘れました。
たとえば、WHERE value = 60 の場合、MYSQL は int 型を探していると判断し、インデックスは失敗します。
一方、値 INT クエリを定義するときに一重引用符 value = '60' を追加すると、インデックスは失敗しません。
その理由: MySQL は文字列と数値の比較に遭遇すると、自動的に文字列を数値に変換し、それを比較します。そして、この変換動作は関数を呼び出します
つまり、最初のメソッドは次と同等です。
where CAST(value AS signed int) = 60;
つまり、関数の使用によるインデックスの無効化が発生しました。
一方、2番目の方法
where value = CAST(60 AS signed int);
今回判定したフィールドは関数を使用しません
ジョイントインデックスの左端以外の一致
上記の共同クエリに関するセクションで紹介されました
使用済み または
OR の前の条件列がインデックス列であるが、OR の後の条件列がインデックス列ではない場合、インデックスは失敗します。
さまざまなフィールド値の比較
WHERE name = 'zhangsan' を使用すると、インデックスは失敗しませんが、
WHERE name_student = name_Teacher を使用すると失敗します (名前が確立されていても) 2 つのフィールド (パラメータ) を比較に使用することはできません
逆範囲操作によりインデックスが無効になる
一般に、待機SQL
操作などの前方範囲のクエリに属する場合>、<、between、like、in...
、インデックスは正常に有効になりますが、待機SQL
操作など逆方向範囲の操作が実行される場合にNOT IN、NOT LIKE、IS NOT NULL、!=、<>...
は問題が発生します。
ただし、IS NULL は前方クエリですが、インデックスも失敗し、IS NOT NULL も失敗します。
MYSQL オプティマイザによるインデックスの無効化
セカンダリ インデックスでの大量の I/O 操作 (テーブルに戻ることによる) は、テーブル全体の直接クエリほど高速ではない場合があります。MYSQL は自動的に低コストの操作を実行します。
例:SELECT * FROM table WHERE a>1000000 and b <500000;
このデータには多くの行があります (数万行と見積もられます)。私たちのプロセスは次のとおりです。
(結合インデックスが > < に遭遇すると停止します。つまり、a はインデックスを使用し、b はそれを使用しません)
aが要件を満たしているかどうかを判定し、aのidでテーブルに戻ってbのデータを取得します。
インデックスを作成する代わりに、
各データを順番に走査し、各データ ページがテーブルにロードされるため、多くの I/O 操作が削減され、コストが大幅に削減されます。
一部のインデックス最適化動作
インデックスカバレッジ
クエリ対象のデータがすべてジョイント インデックスに含まれている場合、複数のテーブルを返す操作を避けるために、値はインデックスから直接取得されます。
たとえば、SELECT name, age FROM table WHERE..... name と age がジョイント インデックスに含まれている場合、通常のセカンダリ インデックス ロジック (対応する主キーを検索し、その主キーを使用してレコード全体のデータ)
インデックスのプッシュダウン
つまり、Server
層でのデータのフィルタリング作業は、処理のためにエンジン層にプッシュダウンされます。
例えば:
SELECT * FROM テーブルの場合、a > 500 かつ b = 20;
この SQL はインデックスを部分的に使用し、インデックスにのみ適用されます。
インデックス プッシュダウンがない場合、その実行プロセスでは、まず a>500 のすべてのエントリが検索され、次にテーブルに戻ってスキャンされて b が 20 に等しいかどうかが判断され、一致する結果がユーザーに返されます。
インデックス プッシュ ダウンでは、a>500 のエントリを検索し、結合インデックスで b=20 を満たすかどうかを判断し、満たさないエントリを除外します。
その後、テーブルに直接戻ってデータを取得するため、テーブルに戻る操作の数が減ります。
MRR (マルチレンジリード) メカニズム
Multi-Range Read
これはメカニズムと呼ばれ、MRR
インデックス プッシュダウンとともにMySQL5.6
バージョンで導入されたパフォーマンス最適化対策でもあります。MRR
最適化とは何ですか?
IO
一般に、実際のビジネスでは、インデックス カバレッジの機能を使用して、バック トゥ テーブル操作の数をできるだけ減らすように努めるべきですが、多くの場合、データをクエリするためにバック トゥ テーブルを実行する必要があることがよくありますが、back-to-table は明らかに多数のディスクにつながりますIO
が、より深刻な点は、多数のディスクリートが存在することです。IO
理解するために例を挙げてみましょう。
SELECT * FROM `zz_student_score` WHERE `score` BETWEEN 0 AND 59;
上記のSQL
作業は非常に単純です. 生徒の成績表にある成績を落としたすべての生徒の情報を照会するだけです. 成績フィールドに共通のインデックスがあると仮定して, 考えてみますと, の実行プロセスは何ですか?これはSQL
?
- ① まずスコアフィールドのインデックス上でスコアノードを見つけ
0
、次にID
リターンテーブルを取得してスコアがゼロの学生情報を取得します。 1
② 再び成績インデックスに戻り、ノードのすべてのポイントを検索し続け、テーブルに戻って1
ポイントの生徒情報を取得します。- ③再びスコアインデックスに戻り、
2
ノードのすべての点を探し続けます…… 0~59
④すべてのポイントの生徒情報を取得するまで、この作業を何度も繰り返します。
このとき、スコアのテーブルデータはディスクスペースのページに0~5
あり、スコアのデータはディスクスペースのページにあり、スコアのデータはディスクスペースのページにあるとする。このとき、クエリのためにテーブルに戻る際に、 2つのページスペースを行ったり来たりすることになりますが、分割されたデータを完全にマージし、その後は1回だけ読み込むことができるため、回数を減らして読み込むことを回避できます。同時に離散性。page_01
5~10
page_02
10~15
page_01
page_01、page_02
0~5、10~15
page_01
IO
IO
この
MRR
機構は主にこの問題を解決するもので、補助インデックスの back-to-table クエリに対して離散性を低減しIO
、IO
random を order に変換するIO
ことでクエリ効率を向上させます。
MRR
仕組みとしては、補助インデックス内のクエリはID
バッファに置かれread_rnd_buffer
、全てのインデックス取得作業が完了した後、またはバッファ内のデータが規定のread_rnd_buffer_size
サイズに達した時点でMySQL
バッファ内のデータが削除されます。この時点で、ソートして順序付けされたID
コレクションを取得しrest_sort
、最後にIO
クラスター/主キー インデックスに移動して、順序に従ってテーブルにデータをクエリします。
インデックススキップスキャン インデックススキップスキャン
制限が多すぎるので理解してください
abc にインデックスを作成しました
SELECT * FROM WHERE b = 1 および c = 1;
本来、この SQL はインデックスを使用すべきではありませんが、MYSQL はインデックスを「スキップ」して強制的にインデックスを使用するのに役立ちます。
実際、MySQL
オプティマイザはジョイント インデックスの最初のフィールドの値を自動的に重複排除し、重複排除に基づいてすべての値を結合して再度チェックします。
インデックスの使用に関するアドバイス
https://juejin.cn/post/7149074488649318431の原文はこちら
- ① クエリ条件として頻繁に使用されるフィールドを考慮して、適切にインデックスを作成する必要があります。
- ② テーブルの主キーと外部キー、またはリンク テーブルのフィールドにはインデックスを付ける必要があります。これにより、リンク テーブル クエリのパフォーマンスが大幅に向上します。
- ③インデックスを付けるフィールドは、インデックスの検索効率を高めるために、一般的な値を十分に区別する必要があります。
- ④ インデックス付きフィールドの値は長すぎてはいけません。より長いフィールドにインデックスを付ける必要がある場合は、プレフィックス インデックスを選択できます。
- ⑤ 結合インデックスを確立するには、左端のプレフィックスの原則に従い、複数のフィールドを優先順位に従って結合する必要があります。
- ⑥ インデックスは順序付けされており、ソート時間を短縮できるため、範囲に応じて値が設定され、並べ替えられ、グループ化されることが多いフィールドに対してインデックスを作成する必要があります。
- ⑦ユニークインデックスの場合、このフィールドがソートに使用されないことが確認できれば、構造体に変更できます
Hash
。 - ⑧単一値インデックスの代わりに結合インデックスを使用してみてください。結合インデックスは複数の単一値インデックス クエリよりも効率的です。
同時に、上記のインデックス作成の原則に加えて、インデックス作成時に注意すべき点がいくつかあります。
❶ 値が頻繁に追加、削除、変更されるフィールドは、変更のたびにインデックス構造を維持する必要があるため、インデックス付けには適していません。
❷ 前の例の性別フィールドのように、フィールドに重複する値が多数ある場合、インデックス付けには適していません。(ただし、選択性が低く、偏ったデータも使用できます。たとえば、原則として 3 つの状態が繰り返されますが、1 つの状態が特に小さく、もう 1 つの状態が非常に大きい場合は、インデックスを使用することもできます (CBO 自動最適化))。
❸ インデックスは計算に参加できないため、関数で頻繁にクエリされるフィールドはインデックス付けには適していません。
❹ テーブル内のインデックスの数はできるだけ多くなく、一般的には3
最大値に制御する必要があり、それを超えることはできません5
。
❺ 結合インデックスを構築するときは、優先順位を考慮する必要があり、クエリ頻度が最も高いフィールドを最初に配置する必要があります。
❻ テーブル内のデータが小さい場合は、インデックスを作成しないでください。データ量が少ない場合、インデックスの維持にコストがかかるためです。
❼ インデックスのフィールド値が間違っている場合、特に主キー インデックスでページ分割が発生するため、インデックスを構築することはお勧めできません。
最初の 8 つのポイントは守る必要はありませんが、最後の 7 つのポイントは避けなければなりません