正直なところ、それを読んだ後、それはあなたをインデックスの失敗から遠ざけるでしょう、それで急いで学ばないでください

みなさん、こんにちは。私の名前はシャオリンです。

職場では、ステートメントのクエリ速度を向上させたい場合は、通常、フィールドにインデックスを作成する必要があります。

しかし、インデックスは万能薬ではありません。インデックスの確立は、クエリステートメントがインデックススキャンを通過できることを意味するものではありません。

注意を怠ると、作成したクエリステートメントによってインデックスが失敗する可能性があるため、全表スキャンが実行されます。クエリ結果は良好ですが、クエリのパフォーマンスは大幅に低下します。

今日は、インデックス障害が発生する6つの一般的なシナリオを紹介します。

実験的な事例で説明するだけでなく、各指標が失敗した理由も明らかになります

行け!

インデックスストレージ構造はどのように見えますか?

まず、インデックスストレージ構造がどのように見えるかを見てみましょう。インデックスのストレージ構造を知っているだけで、インデックス障害の問題をよりよく理解できるからです。

インデックスのストレージ構造は、MySQLが使用するストレージエンジンに関連しています。これは、ストレージエンジンがディスク内のデータの永続化を担当し、異なるストレージエンジンで使用されるインデックスデータ構造が異なるためです。

MySQLのデフォルトのストレージエンジンはInnoDBで、インデックスデータ構造としてB +Treeを使用します。インデックスデータ構造としてB+Treeが選択される理由については、私の記事「MySQLがB+Treeを好む理由」を参照してください。

テーブルを作成するとき、InnoDBストレージエンジンはデフォルトで主キーインデックス、つまりクラスター化インデックスを作成し、他のインデックスはセカンダリインデックスに属します。

MySQLのMyISAMストレージエンジンは、B +ツリーインデックス、Rツリーインデックス、フルテキストインデックスなどのさまざまなインデックスデータ構造をサポートしています。MyISAMストレージエンジンがテーブルを作成するとき、作成された主キーインデックスはデフォルトでB+ツリーインデックスを使用します。

InnoDBとMyISAMはどちらもB+ツリーインデックスをサポートしていますが、データストレージ構造の実装は異なります。違いは次のとおりです。

  • InnoDBストレージエンジン:B+ツリーインデックスのリーフノードはデータ自体を保存します。
  • MyISAMストレージエンジン:B+ツリーインデックスのリーフノードがデータを格納する物理アドレス。

次に、2つのストレージエンジンのインデックスストレージ構造の違いを示す例を示します。

これはt_userテーブルで、idフィールドは主キーインデックスであり、その他は通常のフィールドです。

MyISAMストレージエンジンを使用する場合、次の図に示すように、B +ツリーインデックスのリーフノードには、データの物理アドレス、つまりユーザーデータのポインターが格納されます。

InnoDBストレージエンジンが使用されている場合、次の図に示すように、B+ツリーインデックスのリーフノードはデータ自体を保存します。

InnoDBストレージエンジンは、さまざまなインデックスタイプに応じて、クラスター化インデックス(上の図はクラスター化インデックス)とセカンダリインデックスに分けられます。それらの違いは、クラスター化インデックスのリーフノードが実際のデータを格納し、すべての完全なユーザーデータがクラスター化インデックスのリーフノードに格納され、セカンダリインデックスのリーフノードが実際のデータではなく主キー値を格納することです。データ。

名前フィールドが共通インデックスに設定されている場合、セカンダリインデックスは次の図のようになり、リーフノードはプライマリキー値のみを格納します。

InnoDBストレージエンジンのクラスター化インデックスとセカンダリインデックスのストレージ構造を理解した後、クエリプロセスが使用するインデックスタイプを選択する方法を説明するために、いくつかのクエリステートメントを示します。

「主キーインデックス」フィールドを条件付きクエリとして使用する場合、クエリ対象のデータが「クラスター化インデックス」のリーフノードにある場合、対応するリーフノードは「クラスター化インデックス」のB+ツリーから取得されます。 "、次に、クエリするデータを直接読み取ります。次の文のように:

// id 字段为主键索引
select * from t_user where id=1;

「セカンダリインデックス」フィールドを条件付きクエリとして使用する場合、クエリするデータが「クラスター化インデックス」のリーフノードにある場合は、2つのB+ツリーを取得する必要があります。

  • まず、「セカンダリインデックス」のB +ツリーで対応するリーフノードを見つけ、プライマリキーの値を取得します。
  • 次に、前の手順で取得した主キー値を使用して、「クラスター化インデックス」のB +ツリーで対応するリーフノードを取得し、クエリ対象のデータを取得します。

上記のプロセスは、次のステートメントのように、リターンテーブルと呼ばれます。

// name 字段为二级索引
select * from t_user where name="林某";

「セカンダリインデックス」フィールドを条件付きクエリとして使用する場合、クエリするデータが「セカンダリインデックス」のリーフノードにある場合は、「セカンダリインデックス」のB+ツリーで対応するリーフノードを見つけるだけで済みます。 "、次に読み取ります。クエリ対象のデータのフェッチは、カバーインデックスと呼ばれます。次の文のように:

// name 字段为二级索引
select id from t_user where name="林某";

上記のクエリステートメントの条件はすべてインデックス列を使用するため、インデックスはクエリプロセスで使用されます。

ただし、クエリ条件でインデックス列を使用する場合、クエリプロセスでインデックスを使用する必要があるという意味ではありません。次に、インデックスの実装につながる条件を見てみましょう。全表スキャンが実行されます。

まず、次の実験的なケースでは、私が使用するMySQLのバージョンは8.0.26です。

インデックスに左または左のあいまい一致を使用する

左または左のあいまい一致、つまり%xxまたは%xx%を使用する場合、これら2つの方法によりインデックスが失敗します。

たとえば、次の同様のステートメントで、名前のサフィックスが「フォレスト」であるユーザーをクエリするには、実行プランのtype = ALLは、インデックスを経由せずに全表スキャンを表します。

// name 字段为二级索引
select * from t_user where name like '%林';

名前プレフィックスがフォレストであるユーザーに対するクエリの場合、インデックススキャンが実行されます。実行プランのType = rangeはインデックススキャンを意味し、key=index_nameはindex_nameインデックスが実際になくなったことを確認します。

// name 字段为二级索引
select * from t_user where name like '林%';

likeキーワードの左または左と右のあいまい一致がインデックスを通過できないのはなぜですか?

インデックスB+ツリーは「インデックス値」に従って順番に格納されるため、プレフィックスに従ってのみ比較できます。

たとえば、次のセカンダリインデックスマップは、名前フィールドの順に格納されます。

名前フィールドの前に「forest」が付いているデータ、つまり「forest%」のような名前をクエリするとします。これは、インデックスをスキャンするプロセスです。

  • 最初のノードのクエリの比較:単語Linの拼音のサイズは、最初のノードの最初のインデックス値の単語Chenよりも大きいが、最初のノードの2番目のインデックス値の単語Zhouよりも小さいため、ノードに移動することを選択します2クエリを続行します。
  • ノード2クエリの比較:ノード2の最初のインデックス値のChenワードのピンインサイズは、フォレストワードのピンインサイズよりも小さいため、次のインデックス値を引き続き調べて、ノード2のインデックス値がフォレストワードのプレフィックスなので、リーフノードクエリ、つまりリーフノード4に移動します。
  • ノード4のクエリ比較:ノード4の最初のインデックス値のプレフィックスが単語フォレストと一致するため、この行のデータが読み取られ、プレフィックスフォレストとのインデックス値が一致しなくなるまで、一致が右側に続きます。

'%lin'のような名前を使用してクエリを実行すると、クエリの結果が「Chen Lin、Zhang Lin、Zhou Lin」などになる可能性があるため、どのインデックス値から始めるかわからないため、次のことができます。全表スキャンのみをクエリに渡します。

InnoDBのB+ツリークエリプロセスについて詳しく知りたい場合は、私が書いたこの記事を読むことができます。B+ツリーのノードには何が格納されていますか?データをクエリするプロセスは何ですか?

インデックスで関数を使用する

MySQL独自の関数を使用して目的の結果を取得する場合がありますが、現時点では注意が必要です。この関数をクエリ条件のインデックスフィールドで使用すると、インデックスが失敗します。

たとえば、次のステートメントのクエリ条件では、名前フィールドにLENGTH関数が使用され、実行プランのtype=ALLは全表スキャンを表します。

// name 为二级索引
select * from t_user where length(name)=6;

インデックスの関数が使えないのに、インデックスが使えないのはなぜですか?

インデックスには、関数によって計算された値ではなく、インデックスフィールドの元の値が格納されるため、インデックスを調べる方法はありません。

ただし、MySQL 8.0以降、インデックス機能に関数インデックスが追加されました。つまり、関数によって計算された値に対してインデックスを確立できます。つまり、インデックスの値は関数によって計算された値になります。インデックスをスキャンすることでデータを照会できます。

たとえば、次のステートメントを使用して、length(name)の結果にidx_name_lengthという名前のインデックスを作成します。

alter table t_user add key idx_name_length ((length(name)));

次に、次のクエリステートメントを使用します。この時点でインデックスが取得されます。

インデックスの式を評価する

また、インデックスを使用して、クエリ条件のインデックスに対して式の計算を実行することもできません。

たとえば、次のクエリステートメントでは、実行プランに= ALLと入力し、データが全表スキャンによってクエリされることを示します。

explain select * from t_user where id + 1 = 10;

ただし、クエリステートメントの条件をid = 10 -1に変更すると、インデックスフィールドで式の計算が実行されないため、インデックスクエリを実行できます。

インデックスの式計算にインデックスを使用できないのはなぜですか?

その理由は、インデックスで関数を使用するのと似ています。

インデックスは、id + 1式で計算された値ではなく、インデックスフィールドの元の値を保存するため、インデックスは使用できませんが、インデックスフィールドの値を取り出すことしかできず、式が順番に計算されます。 。条件付き判断。したがって、全表スキャンが使用されます。

一部の学生は、インデックスのこの種の単純な式の計算は、コードの特別な処理の下でインデックススキャンを実行できるはずだと言うかもしれません。たとえば、id + 1=10はid=10-1になります。

はい、実装できますが、MySQLは依然としてこの怠惰を盗み、実装していません。

式の計算にはさまざまな状況があるためかもしれませんが、それぞれを考慮する必要がある場合は、コードが非常に肥大化する可能性があるので、プログラマーにこの種のインデックス障害シナリオを伝えて、プログラマー自身が、クエリ条件のインデックスに対して式の計算を実行しないことを確認します。

インデックスの暗黙的な型変換

インデックスフィールドが文字列型であるが、条件付きクエリで入力パラメータが整数型である場合、実行プランの結果で、このステートメントが全表スキャンを実行することがわかります。

元のt_userテーブルに電話フィールドを追加しました。これはセカンダリインデックスであり、タイプはvarcharです。

次に、条件付きクエリの入力パラメータとして整数を使用します。このとき、実行プランで= ALLと入力すると、データは全表スキャンでクエリされます。

select * from t_user where phone = 1300000001;

ただし、インデックスフィールドが整数型の場合、クエリ条件の入力パラメータが文字列であっても、インデックスは実装されず、インデックススキャンを実行できます。

2番目の例を見てみましょう。idは整数ですが、次のステートメントは引き続きインデックススキャンを実行します。

 explain select * from t_user where id = '1';

最初の例ではインデックスが無効になりますが、2番目の例では無効にならないのはなぜですか?

この理由を理解するには、まずMySQLのデータ型変換ルールを知る必要がありますか?MySQLが文字列を処理用の数値に変換するのか、それとも数値を処理用の文字列に変換するのかを確認するためです。

「Whenmysql45Talks」を読んだとき、「10」> 9の結果を選択することで、MySQLのデータ型変換ルールが何であるかを知る簡単なテスト方法を見ました。

  • MySQLが「文字列」を「数値」に自動的に変換するという規則がある場合、これは10> 9を選択するのと同じです。これは数値の比較であるため、結果は1になります。
  • MySQLが自動的に「数値」を「文字列」に変換するという規則の場合、「10」>「9」を選択するのと同じです。これは文字列の比較であり、文字列の比較サイズはビットごとに高いものから低いものへと比較されます。 (ASCIIコードを押します)この場合、「10」文字列は「1」と「0」の文字の組み合わせに相当します。「1」の文字は小さいため、最初に「1」の文字を「9」の文字と比較します。 「9」文字よりも大きいため、結果は0になります。

MySQLでは、実行の結果は次のとおりです。

上記の結果は1であり、  MySQLが文字列と数値の比較を検出すると、自動的に文字列を数値に変換してから比較を実行することを示しています。

前の例1のクエリステートメントは、全表スキャンを実行することも説明しました。

//例子一的查询语句
select * from t_user where phone = 1300000001;

これは、電話フィールドが文字列であるため、MySQLは文字列を自動的に数値に変換するため、このステートメントは次のようになります。

select * from t_user where CAST(phone AS signed int) = 1300000001;

CAST関数が電話フィールドに作用し、電話フィールドがインデックスであることがわかります。つまり、この関数はインデックスに使用されます前に述べたように、インデックスで関数を使用すると、インデックスが失敗します。

例2のクエリステートメントは、インデックススキャンを実行することを示しました。

//例子二的查询语句
select * from t_user where id = "1";

このとき、文字列部分は入力パラメータであるため、文字列を数値に変換する必要があります。したがって、このステートメントは次のようになります。

select * from t_user where id = CAST("1" AS signed int);

インデックスフィールドは関数を使用せず、CAST関数が入力パラメータに使用されているため、インデックススキャンを実行できることがわかります。

ユニオンインデックスの左端以外の一致

主キーフィールドに基づいて作成されたインデックスはクラスター化インデックスと呼ばれ、共通フィールドに基づいて作成されたインデックスはセカンダリインデックスと呼ばれます。

次に、複数の共通フィールドを組み合わせて作成されたインデックスは、結合インデックスとも呼ばれる結合インデックスと呼ばれます。

ジョイントインデックスを作成するときは、ジョイントインデックス(x、y、z)と(z、y、x)を使用すると異なるため、作成の順序に注意する必要があります。

ジョイントインデックスを正しく使用できるようにするには、左端のマッチングの原則に従う必要があります。つまり、インデックスのマッチングは左端の最初の方法に従って実行されます。

たとえば、a(a、b、c)ジョイントインデックスが作成された場合、クエリ条件が次の場合、ジョイントインデックスを一致させることができます。

  • ここでa=1;
  • ここで、a=1およびb=2およびc=3;
  • ここで、a=1およびb=2;

クエリオプティマイザがあるため、where句のxフィールドの順序は重要ではないことに注意してください。

ただし、クエリ条件が次の場合、左端の一致原則が満たされないため、ジョイントインデックスを一致させることができず、ジョイントインデックスは無効になります。

  • ここでb=2;
  • ここで、c = 3;
  • ここで、b=2およびc=3;

特別なクエリ条件があります。a=1およびc=3の場合、左端の一致と一致しますか?

これは実際には厳密な意味でのインデックスの切り捨てであり、バージョンが異なれば処理も異なります。

MySQL 5.5では、最初のaがインデックスに移動します。ジョイントインデックスで主キーの値が見つかると、テーブルに戻り、主キーのインデックスからデータ行を読み取り、次の値を比較します。 zフィールド。

MySQL 5.6以降、インデックスプッシュダウン関数があります。この関数は、インデックストラバーサルプロセス中にインデックスに含まれるフィールドを最初に判断し、条件を満たさないレコードを直接除外して、テーブルに戻る回数を減らします。 。

一般的な原則は次のとおりです。条件付き判断のために切り捨てられたフィールドがストレージエンジンレイヤーにプッシュダウンされ(cフィールドの値が(a、b、c)ジョイントインデックスにあるため)、修飾されたデータがフィルタリングされますアウトしてからサーバーレイヤーに戻ります。エンジン層で大量のデータが除外されるため、テーブルからデータを読み取って判断する必要がなく、テーブルに戻る回数が減り、パフォーマンスが向上します。

たとえば、次のa=1およびc=0ステートメントでは、実行プランのExtra =Usingindex条件からインデックスプッシュダウン関数を使用できます。

ジョイントインデックスが左端のマッチング原則に従わないのはなぜですか?

その理由は、ジョイントインデックスの場合、データはインデックスの最初の列に従って並べ替えられ、2番目の列は最初の列のデータが同じである場合にのみ並べ替えられるためです。

つまり、ジョイントインデックスでできるだけ多くの列を使用する場合、クエリ条件の各列は、ジョイントインデックスの左端から連続する列である必要があります。2列目だけで検索すると、インデックスをたどることができなくなります。

またはWHERE句で

WHERE句で、ORの前の条件付き列が索引付き列であり、ORの後の条件付き列が索引付き列でない場合、索引は失敗します。

たとえば、次のクエリステートメントでは、idが主キーで、ageが共通の列です。実行プランの結果から、全表スキャンになります。

select * from t_user where id = 1 or age = 18;

これは、ORの意味は、2つのうち1つしか満たすことができないということであり、1つの条件列だけがインデックス列であることは意味がないためです。条件列がインデックス列でない限り、全表スキャンは次のようになります。実行されます。

解決策は、年齢フィールドをインデックスとして設定するのと同じくらい簡単です。

type = index merge、index mergeは、それぞれidとageをスキャンしてから、これら2つの結果セットをマージすることを意味します。これを行う利点は、全表スキャンを回避できることです。

要約する

今日は、インデックス障害が発生する6つの状況を紹介します。

  • 左または左のあいまい一致を使用する場合、つまり、%xxまたは%xx%のように、両方のメソッドでインデックスが失敗します。
  • クエリ条件のインデックス列で関数を使用すると、インデックスが失敗します。
  • クエリ条件のインデックス列で式計算を行う場合、インデックスは使用できません。
  • MySQLが文字列と数値の比較を検出すると、自動的に文字列を数値に変換してから、比較を実行します。文字列がインデックス列で、条件ステートメントの入力パラメータが数値の場合、インデックス列は暗黙的な型変換を受けます。暗黙的な型変換はCAST関数を介して実装されるため、関数を使用するのと同じです。インデックス列なので、インデックスが失敗します。
  • ジョイントインデックスを正しく使用できるようにするには、左端の一致の原則に従う必要があります。つまり、インデックスの一致は左端の最初の方法で実行されます。そうでない場合、インデックスは無効になります。
  • WHERE句で、ORの前の条件付き列が索引付き列であり、ORの後の条件付き列が索引付き列でない場合、索引は失敗します。

最後に、非常に興味深い質問を残しておきます。

  • トピック1:テーブルには複数のフィールドがあり、その中には名前がインデックスフィールド、その他の非インデックス、idに自動インクリメントの主キーインデックスがあります。
  • トピック2:テーブルには2つのフィールドがあり、nameはインデックスフィールドであり、idには自動インクリメントの主キーインデックスがあります。

上記の2つのテーブルに対して、それぞれ次のクエリステートメントを実行します。

  • 「xxx」のような名前のsから*を選択します
  • 「xxx%」のような名前のsから*を選択します
  • 「%xxx」のような名前のsから*を選択します
  • 「%xxx%」のような名前のsから*を選択します

トピック1とトピック2のデータテーブルの場合、インデックスクエリをトリガーするものとトリガーしないものはどれですか。

元のリンク:
https ://mp.weixin.qq.com/s/lEx6iRRP3MbwJ82Xwp675w

著者:小林コーディング

この記事が役に立ったと思われる場合は、リツイート、フォロー、サポートを行うことができます

おすすめ

転載: blog.csdn.net/m0_67645544/article/details/124429552