MySQLクエリの最適化:インデックスマージ

1.なぜインデックスマージがあるのですか?

where内の複数のフィールド、およびそれらの間のANDまたはORに関連する複数の条件(または結合)が存在する可能性がある場合、現時点ではインデックスマージテクノロジを使用できます。簡単に言うと、インデックスマージテクノロジは、実際には、複数のインデックスに対して個別に条件付きスキャンを実行し、次にそれぞれの結果に対してセット操作(Intersect / Union)を実行することです。

MySQL 5.0より前では、テーブルは一度に1つのインデックスしか使用できず、複数のインデックスを使用して条件付きスキャンを同時に実行することはできません。ただし、5.1以降、インデックスマージ最適化テクノロジが導入され、複数のインデックスを使用して同じテーブルで条件付きスキャンを実行できます。

どのような状況で、同時に複数のインデックスを使用することが有益でしょうか?たとえば、WHERE条件は「C1 = 10AND C2 = 100」ですが、C1とC2のインデックスのみであり、そのようなインデックス(C1、C2)はありません。両方のインデックスを同時に使用することは理にかなっています。 。データのバッチをすばやく見つけて、このデータのバッチに対してさらに交差または合計操作を実行できます。この効率は、全表スキャンよりも高速であるか、インデックスの1つのみを使用してスキャンしてからメインインデックスに移動するよりも高速です。クエリを実行しますが、絶対的なものではありません。後で詳しく説明します。

インデックスマージ最適化アルゴリズムには、次の既知の制限があります。

  • 複数の操作や条件付き操作を使用して条件がより複雑で、MySQLが最適な実行プランを使用しない場合は、次の2つの式を使用して条件を変換できます。

(x AND y)OR z =(x OR z)AND(y OR z)

(x OR y)AND z =(x AND z)OR(y AND z)

  • インデックスのマージは、フルテキストインデックスには適用されません。

EXPLAIN出力では、インデックスマージメソッドが[タイプ]列にindex_mergeとして表示されます。この場合、key列には使用されるインデックスのリストが含まれ、key_lenにはこれらのインデックスの最長のキー部分のリストが含まれます。

インデックスマージメソッドは、マージアルゴリズムに従って、Intersect、Union、Sort_unionの3つのタイプに分けられます。EXPLAIN出力のExtraフィールドに表示されます。IntersectとUnionが使用する必要のあるインデックスはRORです。つまり、Rowid Ordered Retrievalです。つまり、異なるインデックスでスキャンされたデータは、同時にROWIDで並べ替える必要があります。ここでのROWIDは、実際にはInnoDBの主キーです(if主キーが定義されていない場合、InnoDBは暗黙的にROWID列を主キーとして追加します)。各インデックスがRORの場合にのみ、マージして並べ替えることができます。もちろん、疑問があるかもしれません。レコードを確認した後、内部ソートが異なります。なぜRORが必要なのですか?はい、Sort-unionがあります。Sort-unionは、ROR以外の各インデックスをソートした後にマージを実行します。MySQLにSort-Intersectがない理由については、後で説明しますが、MariaDBはバージョン5.3以降Sort-Intersectをサポートしています。

次のセクションでは、これらのアルゴリズムについて詳しく説明します。オプティマイザは、利用可能なさまざまなオプションのコスト見積もりに基づいて、さまざまなインデックスマージアルゴリズムと他のアクセス方法のどちらかを選択します。

2、インデックスマージアルゴリズム

  • インデックスマージ交差アクセスアルゴリズム

簡単に言えば、Index Merge Intersectは、複数のインデックス条件スキャンの結果の共通部分です。明らかに、Index Merge Intersectは、複数のインデックス条件の間にAND演算がある場合にのみ表示されます。次の2つの条件またはそれらの組み合わせにより、インデックスマージ交差が実行されます。

ケース1:セカンダリインデックス列が等しい値の一致です。ジョイントインデックスの場合、ジョイントインデックスの各列は等しい値の一致である必要があり、列の一致する部分はありません。

key_part1 = key_part1 = const1 ... AND key_partN = constN

1

key_part1 = key_part1 = const1 ... AND key_partN = constN

ケース2:主キーの範囲条件

SELECT * FROM innodb_table WHERE primary_key <10 AND key_col1 = 20; SELECT * FROM tbl_name WHERE(key1_part1 = 1 AND key1_part2 = 2)AND key2 = 2;

1

2

3

4

5

SELECT * FROM innodb_table

  WHERE primary_key <10 AND key_col1 = 20;

 

SELECT * FROM tbl_name

  WHERE(key1_part1 = 1 AND key1_part2 = 2)AND key2 = 2;

なぜこれらの2つの規制があるのですか?これは、InnoDBのインデックス構造から始める必要があります。InnoDBのセカンダリインデックスの場合、レコードは最初にインデックス列に従って並べ替えられます。セカンダリインデックスがジョイントインデックスの場合、ジョイントインデックスの列に従って並べ替えられます。セカンダリインデックスのレコードは、インデックス列+プライマリキーで構成されます。セカンダリインデックス列の値が同じであるレコードが多数存在する場合があります。インデックス列の値が同じであるレコードは、の値に従って並べ替えられます。主キー。つまり、ポイントはここにあります。セカンダリインデックス列がすべて等しい値であるときに交差インデックスのマージが可能である理由は、この場合にのみ、セカンダリインデックスクエリに基づく結果セットがのプライマリキー値に従ってソートされるためです。各セカンダリインデックスからクエリされた結果セットがすでに主キーに従ってソートされている場合、交差点を見つけるプロセスは簡単です。

次に、交差を見つけるプロセスは次のようになります:2つの結果セットの最小の主キー値を1つずつ取り出し、2つの値が等しい場合はそれを最終的な交差結果に追加し、そうでない場合は現在の小さい主キーを破棄します特定の結果セットの主キー値が使い果たされるまで、主キー値が配置されている結果セットの次の主キー値が比較されます。

次のクエリなど、単一フィールドのセカンダリインデックスに対してインデックスマージ交差処理を実行します。

key1 = xxxおよびkey2 = xxxのタブから*を選択します

1

key1 = xxxおよびkey2 = xxxのタブから*を選択します

次に、プロセスは次のとおりです。

A.key1のセカンダリインデックスに対応するB +ツリーからkey1 = xxxの関連レコードを取り出します。

B.key2のセカンダリインデックスに対応するB +ツリーからkey2 = xxxの関連レコードを取り出します。

C.セカンダリインデックスのレコードはインデックス列+プライマリキーで構成されているため、2つの結果セットのROWID(プライマリキー)値の共通部分を計算できます。

D.前のステップで生成されたROWID値リストに従ってテーブルバック操作を実行します。つまり、クラスター化されたインデックスから指定されたROWID値の完全なユーザー・レコードを取り出して、ユーザーに返します。

交差点の計算方法:

たとえば、ソートされた主キー値をkey1:1、3、5から取得し、ソートされた主キー値をkey2:2、3、4から取得します。

A.最初に2つの結果セットの小さい方の主キー値を取り出して比較します。1<2なので、idx_key1の結果セットの主キー値1を破棄し、後者の3つを取り出して比較します。

B. 3> 2であるため、idx_key2の結果セットの主キー値2は破棄され、後者の3は比較のために取り出されます。

C. 3 = 3であるため、最終的な交差結果に3を追加し、2つの結果セットの背後にある主キー値を引き続き比較します。

D.次の主キー値は等しくないため、最終的な交差結果には主キー値3のみが含まれます。

このプロセスは非常に高速で、時間計算量はO(n)ですが、各セカンダリインデックスの結果セットが主キーで並べ替えられていない場合は、結果セットの主キー値を並べ替えてから、 topそのプロセスはより時間がかかります。

さらに、交差インデックスのマージは複数のセカンダリインデックス間で使用できるだけでなく、インデックスのマージはクラスター化インデックスによって結合することもできます。これは、上記の2番目のケースです。主キーの範囲が次の場合にも使用できます。検索条件で一致する交差インデックスマージインデックスマージを使用します。主キーの範囲が一致するのはなぜですか?それでもアプリケーションシナリオに戻る必要があります。たとえば、次のクエリを見てください。

id> 100およびkey1 = xxxのタブから*を選択します

1

id> 100およびkey1 = xxxのタブから*を選択します

このクエリが交差インデックスを使用してマージできると仮定すると、このクエリはid> 100の条件に従ってクラスター化されたインデックスからいくつかのレコードを取得し、次の条件を通じてkey1のセカンダリインデックスからいくつかのレコードを取得することは当然です。 key1 = xxxをクリックしてから、交差点を再度検索します。実際、これは問題を複雑にし、クラスター化されたインデックスからレコードを取得する必要はありません。セカンダリインデックスのレコードにはすべてプライマリキー値があることを忘れないでください。条件id> 100を直接適用して、key1から取得したプライマリキー値をフィルタリングできます。これはとても簡単です。したがって、主キーを含む検索条件は、他の2次インデックスから取得した結果セットからレコードをフィルタリングするためだけのものであり、等しい値の一致であるかどうかは重要ではありません。

もちろん、上記の1番目と2番目のケースは、交差点インデックスのマージが発生するための必要条件であり、十分条件ではありません。つまり、1番目と2番目のケースが確立されても、交差インデックスのマージが発生しない場合があります。これはオプティマイザによって異なります。たとえば、同等のクエリ条件(つまり、ケース1)では、オプティマイザはkey1またはkey2を直接使用して、特定の検索条件のみに基づいてセカンダリインデックスを読み取り、テーブルに戻った後に別の検索条件をフィルタリングすることもできます。どちらのスキームを使用するか、ここでは2つのクエリ実行メソッド間のコストを分析する必要があります。これは、コストに基づいて推定するオプティマイザーに依存します。

複数のセカンダリインデックスを読み取ると、1つのセカンダリインデックスを読み取るよりもパフォーマンスが低下しますが、セカンダリインデックスを読み取る操作はシーケンシャルI / Oであり、テーブルを返す操作はランダムI / Oであるため、インデックスを作成するときに1つのセカンダリインデックスのみを読み取ると、特に、テーブルに戻す必要のあるレコード数が多く、複数のセカンダリインデックスを読み取った後に交差するレコード数が非常に少ないため、テーブルに戻ることによるパフォーマンスの低下を保存すると、アクセスによるパフォーマンスが低下します。複数のセカンダリインデックス損失が大きい場合、複数のセカンダリインデックスを読み取ってから交差点を取得するコストは、1つのセカンダリインデックスのみを読み取るコストよりも低くなります。逆に。

  • インデックスマージユニオンアクセスアルゴリズム

簡単に言うと、インデックスマージユニオンは複数のインデックス条件のスキャンであり、得られた結果に対してユニオン演算が実行されます。明らかに、OR演算は複数の条件間で実行されます。

次のタイプのwhere条件とそれらの組み合わせは、Index MergeUnionアルゴリズムを使用できます。

ケース1:セカンダリインデックス列が等しい値の一致です。ジョイントインデックスの場合、ジョイントインデックスの各列は等しい値の一致である必要があり、列の一致する部分はありません。

ケース2:主キーの範囲条件

状況3:条件がIndex MergeIntersectを満たす場所

インデックスマージユニオンアルゴリズムは、上記の3つのwhere条件でOR操作を実行するときに使用できます。たとえば、次の例:

SELECT * FROM t1 WHERE key1 = 1 OR key2 = 2 OR key3 = 3; SELECT * FROM innodb_table WHERE(key1 = 1 AND key2 = 2)OR(key3 = 'foo' AND key4 = 'bar')AND key5 = 5;

1

2

3

4

5

6

SELECT * FROM t1

  WHERE key1 = 1 OR key2 = 2 OR key3 = 3;

 

SELECT * FROM innodb_table

  WHERE(key1 = 1 AND key2 = 2)

     OR(key3 = 'foo' AND key4 = 'bar')AND key5 = 5;

最初の例は、3つの単一フィールドインデックスのOR演算であるため、インデックスマージユニオンアルゴリズムを使用できます。

もう少し複雑な2番目の例は、「ケース3」に沿ったものです。つまり、検索条件の特定の部分に対して交差インデックスマージメソッドを使用して取得した主キーセットと、他の方法で取得した主キーセットは次のとおりです。交差しました。それらの中で(key1 = 1 AND key2 = 2)は、Index MergeIntersectと一致しています。(key3 = 'foo' AND key4 = 'bar')AND key5 = 5もIndexMerge Intersectと一致しているため、2つの間のOR演算では当然Index MergeUnionアルゴリズムを使用できます。

オプティマイザは、次の方法でこのクエリを実行できます。

A.最初に検索条件key1 = 1 AND key2 = 2に従って、交差インデックスを使用してマージすることにより、インデックスkey1とkey2から主キーセットを取得します。

B.次に、検索条件key3 = 'foo' AND key4 = 'bar' AND key5 = 5に従って、インデックスkey3とkey4およびkey5から別の主キーのセットを取得します。

C.ユニオンインデックスマージメソッドを使用して上記の2つの主キーセットのユニオンを取得し、テーブルに戻る操作を実行して結果をユーザーに返します。

ただし、次の例ではIndex MergeUnionを使用できません。

SELECT * FROM t1 WHERE key1> 1 OR key2 = 2;

1

2

SELECT * FROM t1

  WHERE key1> 1 OR key2 = 2;

主に範囲照合がkey1で実行されるため、Unionインデックスマージは非主キー範囲クエリをサポートしません。これは主に、範囲クエリによって取得された主キーの順序が正しくないためです。

  • インデックスマージソート-ユニオンアクセスアルゴリズム

Index Merge Unionのインデックスマージの使用条件は厳しすぎるため、各セカンダリインデックス列が等しい値の一致の条件下で使用できるようにする必要があります。たとえば、次の2つの公式ドキュメントに示されている例は次の目的で使用できません。マージインデックスマージ。:

SELECT * FROM tbl_name WHERE key_col1 <10 OR key_col2 <20; SELECT * FROM tbl_name WHERE(key_col1> 10 OR key_col2 = 20)AND nonkey_col = 30;

1

2

3

4

5

SELECT * FROM tbl_name

  WHERE key_col1 <10 OR key_col2 <20;

 

SELECT * FROM tbl_name

  WHERE(key_col1> 10 OR key_col2 = 20)AND nonkey_col = 30;

最初の例について話しましょう。これは、key_col1 <10に従ってkey_col1インデックスから取得されたセカンダリインデックスレコードのプライマリキー値がソートされておらず、key_col2 <20に従ってkey_col2インデックスから取得されたセカンダリインデックスレコードがソートされていないためです。主キーの値は並べ替えられていませんが、key_col1 <10とkey_col2 <20の2つの条件は特に魅力的であるため、次のことができます。

A.最初にkey_col1 <10の条件に従ってkey_col1のセカンダリインデックスからレコードを取得し、レコードのプライマリキー値に従ってそれらを並べ替えます

B. key_col2 <20の条件に従って、レコードは常にkey_col2のセカンダリインデックスから取得され、レコードはレコードのプライマリキー値に従ってソートされます。

C.上記の2つのセカンダリインデックスのプライマリキー値はすべてソートされているため、残りの操作はユニオンインデックスのマージ方法と同じです

上記を最初にセカンダリインデックスレコードのプライマリキー値に従ってソートし、次にSort-Unionインデックスマージと呼ばれるUnionインデックスマージ方法に従って実行します。明らかに、このSort-Unionインデックスマージは純粋なUnionよりも優れています。インデックスのマージにより、セカンダリインデックスレコードのプライマリキー値を並べ替えるプロセスにもう1つのステップが追加されます。

ヒント:Sort-Unionインデックスのマージがあるのに、Sort-Intersectionインデックスのマージがないのはなぜですか?はい、MySQLにはSort-Intersectionインデックスのマージがありません。Sort-Unionの適用可能なシナリオは、検索条件に基づいてセカンダリインデックスから取得されるレコードの数が比較的少ないことです(結論は次のとおりです。or条件を使用する場合2つのインデックスを使用して多くのレコードを取得すると、並べ替えとテーブルへの戻りのコストが非常に高くなるため、テーブルを直接スキャンすることを選択でき、並べ替え交差インデックスをマージする必要はありません)。これらのセカンダリインデックスのレコードが次の場合でも、プライマリキー値を並べ替えるコストはそれほど高くありません。交差インデックスのマージの適用可能なシナリオは、検索条件のみに基づいてセカンダリインデックスから取得するレコードが多すぎることです(結論として、and条件を使用する場合、インデックスを使用すると、すでにごく少数のレコードを返すことができます。別のインデックスを使用する必要はなく、交差インデックスをマージする必要もありません)、結果としてリターンテーブルのオーバーヘッドが多すぎます。マージ後、リターンテーブルのオーバーヘッドを大幅に削減できますが、Sort-Intersectionを追加すると、大きくする必要がありますセカンダリインデックスレコードは、プライマリキー値に従ってソートされます。このコストは、テーブルに戻るクエリよりも高くなる可能性があるため、Sort-Intersectionの導入はありません。

3.インデックスマージのさらなる最適化

インデックスマージを使用すると、複数のインデックスを使用して同時にスキャンし、結果をマージできます。非常に優れた関数のように聞こえますが、Index Merge Intersectが表示される場合は、複合インデックスを作成することでIndex Merge Intersectをさらに最適化できるため、通常、インデックスが適切に確立されていないことを意味します。

たとえば、次のクエリ:

SELECT * FROM t1 WHERE key1 = 1 AND key2 = 2 AND key3 = 3;

1

SELECT * FROM t1 WHERE key1 = 1 AND key2 = 2 AND key3 = 3;

明らかに、最適化のためにこれら3つのフィールドに複合インデックスを作成できるため、3つのインデックスをそれぞれスキャンするのではなく、1つのインデックスを1回スキャンするだけで済みます。

Perconaの公式ウェブサイトには、複合インデックスとインデックスマージを比較した優れた記事があります複数列インデックスとインデックスマージ

4、プロダクションケース

TicketINFテーブルの場合、約100万のデータがあります。上記の学習を組み合わせて、インデックスのマージがどのように機能するかを確認してください。簡単なORクエリから始めましょう。

mysql> Explain SELECT * FROM TicketINF WHERE memberSysId = 'tb.main' OR memberId = '123456'; + ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- + | id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ| + ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- + | 1 | シンプル| TicketINF | index_merge | idx_memberSysId_memberSysUserId、idx_memberId | idx_memberSysId_memberSysUserId、idx_memberId | 153、303 | NULL | 7449 | sort_union(idx_memberSysId_memberSysUserId、idx_memberId);を使用します。whereを使用する| + ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- +セット内の1行(0.01秒)

1

2

3

4

5

6

7

mysql> Explain SELECT * FROM TicketINF WHERE memberSysId = 'tb.main' OR memberId = '123456';

+ ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- +

| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|

+ ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- +

| 1 | シンプル| TicketINF | index_merge | idx_memberSysId_memberSysUserId、idx_memberId | idx_memberSysId_memberSysUserId、idx_memberId | 153,303 | NULL | 7449 | sort_union(idx_memberSysId_memberSysUserId、idx_memberId);を使用します。whereを使用する|

+ ------ + ------------- + ----------- + ------------- +- -------------------------------------------- + ----- ----------------------------------------- + -------- -+ ------ + ------ + ---------------------------------- ------------------------------------------- +

1 row in set (0.01 sec)

根据执行计划看出,两个字段都使用到了索引(两个普通索引,memberSysId字段是组合索引的第一个字段,memberId是一个普通索引),使用了索引合并技术,且使用的是sort-union合并算法。

根据我们上面说的,如果查询字段是复合索引的第二个字段,那么是无法使用到索引的,如下执行计划。

mysql> explain SELECT * FROM TicketINF WHERE memberSysUserId = '456789' OR memberId = '123456'; +------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | TicketINF | ALL | idx_memberId | NULL | NULL | NULL | 850450 | Using where | +------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+ 1 row in set (0.00 sec)

1

2

3

4

5

6

7

mysql> explain SELECT * FROM TicketINF WHERE memberSysUserId = '456789' OR memberId = '123456';

+------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

| id   | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra       |

+------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

|    1 | SIMPLE      | TicketINF | ALL  | idx_memberId  | NULL | NULL    | NULL | 850450 | Using where |

+------+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

1 row in set (0.00 sec)

然后对上一个查询再加上一个 AND 运算。

mysql> explain SELECT * FROM TicketINF WHERE (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456'; +------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+ | 1 | SIMPLE | TicketINF | index_merge | idx_memberSysId_memberSysUserId,idx_memberId | idx_memberSysId_memberSysUserId,idx_memberId | 456,303 | NULL | 2 | Using union(idx_memberSysId_memberSysUserId,idx_memberId); Using where | +------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+ 1 row in set (0.00 sec)

1

2

3

4

5

6

7

mysql> explain SELECT * FROM TicketINF WHERE (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456';

+------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+

| id   | select_type | table     | type        | possible_keys                                | key                                          | key_len | ref  | rows | Extra                                                                  |

+------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+

|    1 | SIMPLE      | TicketINF | index_merge | idx_memberSysId_memberSysUserId,idx_memberId | idx_memberSysId_memberSysUserId,idx_memberId | 456,303 | NULL |    2 | Using union(idx_memberSysId_memberSysUserId,idx_memberId); Using where |

+------+-------------+-----------+-------------+----------------------------------------------+----------------------------------------------+---------+------+------+------------------------------------------------------------------------+

1 row in set (0.00 sec)

从执行计划来看,同样使用到了索引合并,但这回使用的合并算法是union。

再往下,我们再次添加一个 OR 运算,增加一个mobile字段的检索。

mysql> explain SELECT * FROM TicketINF WHERE (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456' OR mobile = '12100001111'; +------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | TicketINF | ALL | idx_memberSysId_memberSysUserId,idx_memberId | NULL | NULL | NULL | 850385 | Using where | +------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+ 1 row in set (0.00 sec)

1

2

3

4

5

6

7

mysql> explain SELECT * FROM TicketINF WHERE (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456' OR mobile = '12100001111';

+------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+

| id   | select_type | table     | type | possible_keys                                | key  | key_len | ref  | rows   | Extra       |

+------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+

|    1 | SIMPLE      | TicketINF | ALL  | idx_memberSysId_memberSysUserId,idx_memberId | NULL | NULL    | NULL | 850385 | Using where |

+------+-------------+-----------+------+----------------------------------------------+------+---------+------+--------+-------------+

1 row in set (0.00 sec)

从执行计划上看,走了全表扫描,一个索引也没有用上,更没有用上索引合并。此条SQL在此表执行时间为2s左右,导致慢查询过多,服务器负载到了200。

此时,我们再给mobile字段增加一个普通索引,再次看执行计划,如下。

mysql> explain SELECT * FROM TicketINF WHERE (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456' OR mobile = '12100001111'; +------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+ | 1 | SIMPLE | TicketINF | index_merge | idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile | idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile | 456,303,153 | NULL | 3 | Using union(idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile); Using where | +------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+ 1 row in set (0.00 sec)

1

2

3

4

5

6

7

mysql> explain SELECT * FROM TicketINF WHERE  (memberSysId = 'tb.main' AND memberSysUserId = '123456') OR memberId = '123456' OR mobile = '12100001111';

+------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+

| id   | select_type | table     | type        | possible_keys                                           | key                                                     | key_len     | ref  | rows | Extra                                                                             |

+------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+

|    1 | SIMPLE      | TicketINF | index_merge | idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile | idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile | 456,303,153 | NULL |    3 | Using union(idx_memberSysId_memberSysUserId,idx_memberId,idx_mobile); Using where |

+------+-------------+-----------+-------------+---------------------------------------------------------+---------------------------------------------------------+-------------+------+------+-----------------------------------------------------------------------------------+

1 row in set (0.00 sec)

从结果看可以,又可以使用上了索引合并,把相关的三个索引都使用上了。

原文链接  http://www.ywnds.com/?p=14468

<参考>

http://www.cnblogs.com/digdeep/archive/2015/11/18/4975977.html

https://dev.mysql.com/doc/refman/5.7/en/index-merge-optimization.html#index-merge-intersection

おすすめ

転載: blog.csdn.net/sqiucheng/article/details/87867037