MySQL インデックスの最適化とクエリの最適化

1. インデックス失敗のケース

1 つの完全な値の一致

2 最適な左プレフィックス ルール

3 主キーの挿入順序

このデータ ページがいっぱいの場合は、現在のページを 2 つのページに分割し、このページ内の一部のレコードを新しく作成したページに移動する必要があります。ページ分割とレコードのシフトはパフォーマンスの低下を意味します。したがって、このような不要なパフォーマンスの低下を可能な限り回避したい場合は、挿入されたレコードの主キーの値を順番に増やして、そのようなパフォーマンスの低下が発生しないようにするのが最善です。
そこで、主キーに AUTO_INCREMENT を持たせ、テーブルの主キーを手動で挿入するのではなく、ストレージ エンジンが独自に生成できるようにすることをお勧めします。

4 計算、関数、型変換 (自動または手動) によりインデックスエラーが発生する

5 型変換によりインデックスが失敗する

6 範囲条件の右側の列インデックスが無効です

7 等しくない (!= または <>) インデックスが無効です

8 が null の場合はインデックスを使用でき、null でない場合はインデックスを使用できません

9 ワイルドカード文字 % で始まるようなインデックスは無効です

10 OR の前後にインデクスのない列があり、インデクスが無効です。

11 データベースおよびテーブルの文字セットは一律に utf8mb4 を使用します。

utf8mb4(バージョン5.5.3以降でサポート)を統一することで互換性が向上し、文字セットを統一することで文字セット変換による文字化けを回避できます。比較する前に異なる文字セットを変換する必要があるため、インデックスエラーが発生します。

2. 結合文の原理

EXPLAIN SELECT * FROM t1 STRAIGHT_JOIN t2 ON (t1.a=t2.a);

join ステートメントを直接使用する場合、MySQL オプティマイザは駆動テーブルとしてテーブル t1 または t2 を選択する可能性があり、これは SQL ステートメントの分析の実行プロセスに影響を与えます。実行中のパフォーマンスの問題の分析を容易にするために、代わりに Straight_join を使用して、MySQL が固定接続メソッドを使用してクエリを実行できるようにします。これにより、オプティマイザーは指定した方法でのみ結合します。このステートメントでは、t1 は駆動テーブル、t2 は被駆動テーブルです。

 このステートメントでは、駆動テーブル t2 のフィールド a にインデックスがあり、結合プロセスではこのインデックスが使用されることがわかります。そのため、このステートメントの実行フローは次のようになります。 1. データ R の行を読み取ります。テーブル
t1 ;
2. データ行 R から a フィールドを取り出し、テーブル t2 で検索します;
3. テーブル t2 の条件を満たす行を取り出し、結果セットの一部として R を含む行を形成します;
4ステップ 1 ~ 3 を繰り返し、テーブル t1 の終わりまでループが終了します。
このプロセスでは、最初にテーブル t1 を走査し、次にテーブル t2 に移動して、テーブル t1 から取得したデータの各行の a 値に基づいて条件を満たすレコードを検索します。形式的には、このプロセスはプログラムを作成するときのネストされたクエリに似ており、駆動テーブルのインデックスを使用できるため、「インデックス ネスト ループ結合」 (略して NLJ) と呼ばれます。
対応するフローチャートは次のとおりです。

このプロセスでは:
1. ドライバー テーブル t1 でフル テーブル スキャンが実行されます。このプロセスでは 100 行のスキャンが必要です;
2. 各行 R について、ツリー検索プロセスを使用して、a フィールドに基づいてテーブル t2 が検索されます。構築するデータは 1 対 1 対応であるため、各検索プロセスで 1 行のみがスキャンされ、合計 100 行がスキャンされます; 3. したがって、実行プロセス全体でスキャンされる行の合計数は 200 になります
。 。 

結論:
join ステートメントを使用すると、強制的に複数の単一テーブルに分割して SQL ステートメントを実行するよりもパフォーマンスが向上します。join
 ステートメントを使用する場合は、小さなテーブルを駆動テーブルとして使用する必要があります。

ドリブン テーブルの JOIN フィールドにインデックスが作成されていることを確認します。

JOIN が必要なフィールドでは、データ型が完全に一貫している必要があります。
LEFT JOIN を実行するときは、小さなテーブルを駆動テーブルとして選択し、大きなテーブルを駆動テーブルとして選択します。外側のループの数を減らします。
INNER JOIN の場合、MySQL は小さな結果セットを持つテーブルを駆動テーブルとして自動的に選択します。
複数のテーブルを直接関連付けることができる場合は、サブクエリを使用せずに直接関​​連付けるようにしてください。(クエリ数を減らす)
サブクエリの使用は推奨せず、サブクエリのSQLを分割して複数のクエリのプログラムを結合するか、サブクエリをJOINで置き換えることを推奨します。
派生テーブルではインデックスを作成できません。

 3. サブクエリの最適化

MySQL はバージョン 4.1 以降のサブクエリをサポートしています。サブクエリを使用すると、SELECT ステートメントのネストされたクエリを実行できます。つまり、1 つの SELECT クエリの結果が別の SELECT ステートメントの条件として機能します。サブクエリは、論理的に複数の手順を一度に完了する必要がある多くの SQL 操作を完了できます。
サブクエリは MySQL の重要な機能であり、SQL ステートメントを通じてより複雑なクエリを実装するのに役立ちます。ただし、サブクエリの実行効率は高くありません。理由:
① サブクエリを実行するとき、MySQL は内部クエリ ステートメントのクエリ結果用の一時テーブルを作成する必要があり、その後、外部クエリ ステートメントで一時テーブルのレコードをクエリします。クエリが完了すると、これらの一時テーブルは取り消されます。これにより、CPU と IO リソースが過剰に消費され、低速なクエリが大量に生成されます。
② サブクエリの結果セットに格納される一時テーブルは、メモリ一時テーブルであってもディスク一時テーブルであってもインデックスを持たないため、クエリのパフォーマンスにある程度の影響を受けます。
③ より大きな結果セットを返すサブクエリの場合、クエリのパフォーマンスへの影響は大きくなります。
MySQL では、サブクエリの代わりに結合 (JOIN) クエリを使用できます。結合クエリは一時テーブルを作成する必要がなく、サブクエリよりも高速であり、クエリでインデックスを使用するとパフォーマンスが向上します。

結論: NOT IN や NOT EXISTS は使用せず、代わりに LEFT JOIN xxx ON xx WHERE xx IS NULL を使用してください。

4. ソートの最適化

1. SQL では、WHERE 句と ORDER BY 句でインデックスを使用して、WHERE 句での全テーブル スキャンと ORDER BY 句での FileSort ソートを回避できます。もちろん、場合によっては、テーブル全体のスキャンや FileSort の並べ替えが必ずしもインデックス作成より遅いとは限りません。ただし、一般に、クエリの効率を向上させるためには、依然としてこれを回避する必要があります。
2. Index を使用して ORDER BY ソートを完了してみます。WHERE と ORDER BY の後に同じ列が続く場合は、単一のインデックス列を使用し、
それらが異なる場合は、結合インデックスを使用します。
3. Indexが使用できない場合は、FileSortメソッドのチューニングが必要です。

5. ファイルソートアルゴリズム: 双方向ソートと一方向ソート

双方向ソート (低速)
MySQL 4.1 は以前は双方向ソートを使用していました。これは文字通りディスクを 2 回スキャンして最終的にデータを取得し、行ポインタを読み取って列ごとに並べ替え、それらを並べ替えてから、並べ替えられたリストをスキャンすることを意味します
。リスト内の値をリストから再読み込みし、
対応するデータを出力します。

ディスクから並べ替えフィールドを取得し、バッファー内で並べ替えてから、ディスクから他のフィールドを取得してデータのバッチを取得します。ディスクを 2 回スキャンする必要があります。IO には非常に時間がかかるため、2 番目のタイプは、 mysql4.1. 改良されたアルゴリズムは一方向ソートです。
シングルパス ソート (高速) では、
クエリに必要なすべての列をディスクから読み取り、列ごとの順序に従ってバッファ内でソートし、ソートされたリストをスキャンして出力します。これはより効率的で、2 回目の読み取りを回避します。データ。また、ランダム IO をシーケンシャル IO に変換しますが、各行をメモリに保存するため、より多くのスペースを使用します。

結論とそこから派生する問題
シングルチャネルは最後に登場するため、通常はデュアルチャネルより優れていますが、シングルチャネルを使用する場合には問題があります 最適化戦略 1.
sort_buffer_size
を大きくしてみます
2. max_length_for_sort_data を大きくしてみます
3. * を選択してくださいでのご注文はタブーです。必要なフィールドのみをクエリすることをお勧めします。

6. GROUP BY の最適化

group by でのインデックスの使用原理は order by とほぼ同じで、インデックスを使用した絞り込み条件が存在しない場合でも、group by ではインデックスを直接使用できます。
group by は、インデックス構築のための最も左のプレフィックス ルールに従って、最初にソートしてからグループ化します。
インデックス列を使用できない場合は、max_length_for_sort_data パラメーターと sort_buffer_size パラメーターの設定を増やします。
haveよりwhereの方が効率が良いので、whereに条件が書ける場合はhavingに書かないでください。
order by の使用を減らすか、ソートせずにソートするか、ターミナルにソート機能を組み込みます。Order by、group by、distinct などのステートメントはより多くの CPU を消費するため、データベースの CPU リソースは非常に貴重です。
order by、group by、distinct などのクエリ ステートメントが含まれます。where 条件でフィルタリングされた結果セットは 1,000 行以内に収める必要があり、そうしないと SQL が非常に遅くなります。

7. ページングクエリを最適化する

インデックスの並べ替えとページング操作を完了し、最後に主キーに従って元のテーブル クエリに必要な他の列の内容を関連付けます。

8. カバーインデックスの優先順位付け

インデックスは行を検索する効率的な方法ですが、データベースはインデックスを使用して列のデータを検索することもできるため、行全体を読み取る必要はありません。結局のところ、インデックス リーフ ノードにはインデックス付けされたデータが格納され、インデックスを読み取ることで目的のデータが取得できる場合は、行を読み取る必要はありません。クエリ結果を満たすデータを含むインデックスをカバーインデックスと呼びます。

クエリの SELECT、JOIN、および WHERE 句で使用されるすべての列を含む非クラスター化複合インデックスの形式 (つまり、インデックスが作成されるフィールドがクエリ条件に関係するフィールドをたまたまカバーしている)。

簡単に言うと、インデックス列 + 主キーには、SELECT から FROM までクエリされた列が含まれます。

インデックスをカバーすることの長所と短所

利点:
1. Innodb テーブル インデックスの二次クエリ (テーブル リターン) を回避します。
2. ランダム IO をシーケンシャル IO に変換して、クエリ効率を高速化できます。
欠点:
インデックス フィールドのメンテナンスには常にコストがかかります。したがって、カバーインデックスをサポートするために冗長インデックスを構築する際には、考慮すべきトレードオフがあります。これはビジネス DBA、またはビジネス データ アーキテクトの仕事です

9. プレフィックスインデックス

接頭辞インデックスがカバリングインデックスに及ぼす影響

プレフィックス インデックスを使用すると、クエリのパフォーマンスを最適化するためのカバー インデックスが不要になります。これは、
プレフィックス インデックスを使用するかどうかを選択するときに考慮する必要がある要素でもあります。

10. インデックスのプッシュダウン

Index Condition Pushdown (ICP) は MySQL 5.6 の新機能で、インデックスを使用してストレージ エンジン層でデータをフィルタリングする最適化方法です。ICP を使用すると、ストレージ エンジンがベース テーブルにアクセスする回数と、MySQL サーバーがストレージ エンジンにアクセスする回数を減らすことができます。

10.1 ICP インデックス スキャンを使用しないプロセス:

ストレージ層:インデックスキーの条件を満たすインデックスレコードに対応するレコードの行全体のみを取り出してサーバー層に返す サーバー層
:返されたデータは後続のwhere条件でフィルタリングされ、最後の行まで返されます。

10.2 ICP スキャンを使用するプロセス:

ストレージ層:
まず、インデックスキーの条件を満たすインデックスの記録間隔を決定し、次にフィルターするインデックスに対してインデックスフィルターを使用します。インデックスフィルター条件を満たすインデックスレコードのみがテーブルに返され、レコードの行全体がサーバー層に返されます。インデックス フィルター条件を満たさないインデックス レコードは破棄され、テーブル層やサーバー層には返されません。
サーバー層:
返されたデータの最終的なフィルタリングにはテーブル フィルタ条件を使用します。

 

使用前と使用後のコストの違い。使用前は
、ストレージ レイヤーはインデックス フィルターで除外する必要がある多くの行のレコードを返しました。ICP の
使用後は、インデックス フィルターの条件を満たさないレコードが直接削除されるため、インデックス フィルターの必要性がなくなりました。それらはテーブルに返され、サーバー層に渡されます。
ICP の高速化効果は、ストレージ エンジン内で ICP によってフィルタリングされたデータの割合によって異なります。

 10.3 ICPの使用条件

ICPの使用条件:
①セカンダリインデックス(セカンダリインデックス)のみに使用可能です。 ②explainで
表示される実行プランの型値(結合型)がrange、ref、eq_ref、ref_or_nullのいずれかである。
③ ICP ですべての where 条件をフィルタリングできるわけではないため、where 条件のフィールドがインデックス列にない場合でも、where フィルタリングのためにテーブル全体のレコードをサーバーに読み込む必要があります。
④ ICP は MyISAM および InnnoDB ストレージ エンジンに使用できます。
⑤ MySQL バージョン 5.6 はパーティション テーブルの ICP 機能をサポートしていません。バージョン 5.7 からサポートされます。
⑥ SQL がカバリングインデックスを使用する場合、ICP 最適化手法はサポートされません。

 非主キー インデックスに対するインデックス プッシュダウンの最適化により、テーブルの戻り数が効果的に削減され、クエリ効率が大幅に向上します。日常業務では、ビジネスの状況に応じてインデックスを最適化することでインデックス プッシュダウンを実現し、ビジネス スループットを向上させることができます。

11. 通常のインデックスと一意のインデックス

パフォーマンスの観点から、一意のインデックスと通常のインデックスのどちらを選択する必要がありますか? 選択の基準は何ですか?
ID として主キー列を持つテーブルがあるとします。テーブルにはフィールド k があり、k にはインデックスがあります。フィールド k の値は繰り返されないとします。

このテーブルのテーブル作成ステートメントは次のとおりです。


mysql> create table test(
id int primary key,
k int not null,
name varchar(16),
index (k)
)engine=InnoDB;

表中のR1~R5の(ID,k)値は、それぞれ(100,1)、(200,2)、(300,3)、(500,5)、(600,6)となります。

11.1 クエリプロセス

クエリを実行するステートメントが次であると仮定します。

k=5 のテストから ID を選択します。
通常のインデックスの場合、条件を満たす最初のレコード (5,500) を見つけた後、k=5 条件を満たさない最初のレコードが見つかるまで次のレコードを見つける必要があります。
一意のインデックスの場合、インデックスが一意性を定義するため、条件を満たす最初のレコードが見つかった後に検索が停止します

では、この違いによって生じるパフォーマンスの違いは何でしょうか? 答えは、最小限です。

11.2 アップデートプロセス

通常のインデックスと一意のインデックスが更新ステートメントのパフォーマンスに与える影響を説明するために、変更バッファを紹介します。
データ ページを更新する必要がある場合、データ ページがメモリ内にあれば直接更新されます。データ ページがまだメモリ内にない場合、InooDB はデータの一貫性に影響を与えることなく、これらの更新操作を変更バッファにキャッシュします。このデータ ページをディスクから読み取る必要はありません。次のクエリでこのデータ ページにアクセスする必要がある場合、データ ページをメモリに読み取り、変更バッファ内のこのページに関連する操作を実行します。このようにして、データロジックの正確性を保証できます。
変更バッファ内の操作を元のデータ ページに適用し、最新の結果を取得するプロセスはマージと呼ばれます。このデータ ページにアクセスするときにマージをトリガーするだけでなく、システムには定期的にマージするバックグラウンド スレッドがあります。データベースの通常のシャットダウン中に、マージ操作も実行されます。
更新操作を最初に変更バッファーに記録してディスク読み取りを減らすことができれば、ステートメントの実行速度が大幅に向上します。さらに、データをメモリに読み込むにはバッファ プールを占有する必要があるため、この方法ではメモリの占有を回避し、メモリ使用率を向上させることもできます。
変更バッファを使用して一意のインデックスを更新することはできず、実際には通常のインデックスのみが使用できます。

11.3 変更バッファの使用シナリオ

1. 通常のインデックスとユニークなインデックスのどちらを選択するか? 実際、これら 2 種類のインデックスの間でクエリ機能に違いはありませんが、主に考慮すべき点は
更新パフォーマンスへの影響です。したがって、通常のインデックスを選択することをお勧めします。2. 実際に使用してみると、通常のインデックスと変更バッファを組み合わせて使用​​すると、大量のデータを含むテーブルの更新を明らかに最適化
できることがわかります。3. すべての更新の直後にこのレコードに対するクエリが続く場合は、変更バッファを閉じる必要があります。他の場合には、変更バッファーによって更新パフォーマンスが向上することがあります。4. 一意のインデックスは変更バッファの最適化メカニズムを使用できないため、ビジネスが許容できる場合は、パフォーマンスの観点から非一意のインデックスを優先することをお勧めします。しかし、「ビジネスが保証されない可能性がある」場合はどうすればよいでしょうか? まず、ビジネスの正確さが優先されます。パフォーマンスの問題を議論するときは、「ビジネスコードが重複データを書き込まないことが保証されている」という前提があります。ビジネスがこれを保証できない場合、またはデータベースに制約を設ける必要がある場合は、一意のインデックスを作成する以外に選択肢はありません。その後、一部の「アーカイブ ライブラリ」シナリオでは、一意のインデックスの使用を検討できます。たとえば、オンライン データは半年だけ保持する必要があり、その後は履歴データがアーカイブに保存されます。現時点では、アーカイブされたデータにより、一意のキーの競合がないことが保証されています。アーカイブ効率を向上させるために、テーブルの一意のインデックスを通常のインデックスに変更することを検討できます。




12. その他のクエリ最適化戦略

12.1 EXISTS と IN の違い:

In: は外部テーブルと内部テーブルの間のハッシュ接続であり、existing は外部テーブル上のループ ループであり、ループがループするたびに内部テーブルがクエリされます。

同じようなサイズの 2 つのテーブルをクエリする場合、In を使用する場合と、exists を使用する場合にほとんど違いはありません。

2 つのテーブルのうちの一方が小さく、もう一方が大きい場合は、大きいサブクエリ テーブルには存在を使用し、小さいサブクエリ テーブルには In を使用する方が効率的です

つまり 、INは外面が大きく内テーブルが小さい場合に適しており、EXISTSは外面が小さく内テーブルが大きい場合に適しており、効率が高くなります。



12.2 COUNT(*) および COUNT (特定のフィールド) の効率
count(*) には、行数に等しいすべての列が含まれます。結果をカウントするとき、列の値が NULL であっても無視されません
。すべての列を無視することを含みます。コード行を表すには 1 を使用します。結果をカウントするとき、列の値は
NULL であり、無視されません。NULL の場合、統計は収集されません。

効率の観点からは、# COUNT (※) ≈ COUNT(1) > COUNT (フィールド) であり、COUNT (※) は SQL92 で定義されている標準統計構文であるため、COUNT(*) の使用をお勧めします。

12.3 テーブル クエリの SELECT(*) については
、フィールドを指定することを推奨します。クエリのフィールド リストとして * を使用しないでください。SELECT <フィールド リスト> を使用することをお勧めします。理由を確認してください: ① 解析プロセス
中, MySQL はデータ ディクショナリをクエリして "* "すべてのカラム名を順番に変換すると、多くのリソースと時間が消費されます。
②カバリングインデックスは使用できません


12.4 最適化に対する LIMIT 1 の影響は、
テーブル全体をスキャンする SQL ステートメントに対してです。結果セットが 1 つだけであることが確実な場合、LIMIT 1 を追加すると、結果が見つかってもスキャンは続行されません。クエリを高速化します。
データ テーブルでフィールドに対して一意のインデックスが確立されている場合は、そのインデックスを介してクエリを実行できます。テーブル全体がスキャンされない場合は、LIMIT 1 を追加する必要はありません。


12.5 COMMIT をもっと使用する
可能であれば、プログラム内で COMMIT をできるだけ多く使用してください。これにより、プログラムのパフォーマンスが向上し、COMMIT によって解放されるリソースによる需要が軽減されます。
COMMIT によって解放されるリソース:
データのリカバリに使用されるロールバック セグメントに関する情報、
プログラム ステートメントによって取得されるロック、
REDO/UNDO ログ バッファ内のスペース、
上記 3 つのリソースを管理する内部コスト

おすすめ

転載: blog.csdn.net/shenBaoYun/article/details/125722554