ナビゲーション:
目次
3.2.1 混合ビジネスサブテーブル、ホットデータサブテーブルとコールドデータサブテーブル
3.2.2 結合クエリを中間リレーショナルテーブルに変更する
3.3.10 MySQL5.6でサポートされているインデックスプッシュダウンを使ってみる
3.3.11 より多くの書き込みが必要で、読み取りが少なくなるシナリオでは、通常のインデックスを使用してみてください。
MySQL のチューニングは主に、監視と警告、遅い SQL のトラブルシューティング、MySQL のチューニングの 3 つのステップに分かれています。
1. 監視と警報
監視ツール (Prometheus+Grafana など) が MySQL を監視し、クエリのパフォーマンスが遅いことを検出すると、アラームが運用および保守担当者に通知します。
2. 遅い SQL のトラブルシューティング
2.1 スロークエリログを有効にする
遅いクエリの数を表示します。
show status like 'slow_queries';
スロー クエリ ログを有効にして、スロー クエリのしきい値を変更します。
set slow_query_log='ON'; #开启慢查询日志
set long_query_time = 1; #设置慢查询阈值
2.2 最も遅い SQL を見つける
低速クエリ ログ分析ツール mysqldumpslow は、最も遅いステートメントを見つけます。
mysqldumpslow コマンドの特定のパラメータは次のとおりです。
- -a: 数値を N に抽象化したり、文字列を S に抽象化したりしません。
- -s: 並べ替える方法を示します。
- c: 訪問数
- l: ロック時間
- r: リターンレコード
- t: クエリ時間
- al: 平均ロック時間
- ar: 返されるレコードの平均数
- at: 平均クエリ時間 (デフォルト モード)
- ac: 平均クエリ数
- -t: 以前のデータ数を返します。
- -g: 大文字と小文字を区別しない、通常の一致パターンが続きます。
例: クエリ時間で並べ替え、上位 5 つの遅いクエリ SQL ステートメントを表示します。
#命令行,按照查询时间排序,查看前五条 慢查询SQL 语句
mysqldumpslow -s t -t 5 /var/lib/mysql/xxx-slow.log
2.3 クエリプランの分析
explan は SQL 実行プラン (アクセス タイプ、レコード数、インデックスの長さなど) を分析します。主に次のフィールドに焦点を当てます。
- possible_keys:クエリに使用できるインデックス
- key:実際に使用されるインデックス
- key_len:実際に使用されるインデックスのバイト長。
- type:アクセス タイプ。インデックスがあるかどうかを確認します。all (テーブル全体のスキャン)、ref (一意でないインデックスのヒット)、const (主キー/一意のインデックスのヒット)、range (範囲インデックス クエリ)、index_merge (複数のインデックスの使用)、system (レコード行の高速クエリ) 。
- 追加:追加情報。インデックスがあるかどうかを確認します。
- インデックスを使用:インデックスをカバーし、テーブルには戻りません。
- filesort を使用:追加の並べ替えが必要です。ソートにはインデックスソートとファイルソートがあり、一般的にはインデックスソートの方が高速で、ディープページングなどクエリデータが大量にある場合はファイルソートの方が高速です。
- インデックス条件の使用:インデックス プッシュダウン。MySQL5.6のサポートを開始しました。結合インデックスのフィールドがファジー クエリ (非左ファジー) の場合、フィールドの判定後、次のいくつかのフィールドを直接判定できます。判定がフィルタリングされた後、テーブルに戻ってフィールドの条件を判定します。ジョイントインデックスには含まれていません。
- where:インデックスを作成しないフルテーブルスキャンを使用します。
実行計画における各列の役割
ID 各 SELECT 句または結合操作には一意の番号が割り当てられます。番号が小さいほど優先度が高くなります。同じ ID を持つステートメントはグループとして考慮されます。idがNULLの場合は独立したサブクエリを意味し、メインクエリよりもサブクエリの優先度が高くなります。 選択タイプ クエリのタイプ。メインクエリ(プライマリ)、共通クエリ(単純)、結合クエリ、サブクエリ(サブクエリ)、派生(テーブルからの一時的なサブクエリ)、ユニオン(ユニオン後のクエリ)、ユニオン result() テーブル テーブル名。現在の行のデータがどのテーブルに属しているかを表示します。 パーティション パーティション情報が一致しています。テーブルがパーティション化されていない場合は NULL。 タイプ アクセス タイプでは、インデックス作成やテーブル全体のスキャンなどの方法に従ってクエリ最適化戦略が実行されます。all (テーブル全体のスキャン)、ref (一意でないインデックスのヒット)、const (主キー/一意のインデックスのヒット)、range (範囲インデックス クエリ)、index_merge (複数のインデックスの使用)、system (レコード行の高速クエリ) 。 possible_keys 使用できるインデックス。MySQL がクエリに使用できるインデックスをリストします。
列に possible_keys が 1 つだけある場合は、通常、クエリが効率的であることを意味します。
このカラムに複数の possible_key があり、MySQL がそのうちの 1 つだけを使用する場合、このカラムにジョイント インデックスを追加する必要があるかどうかを検討する必要があります。
鍵 実際に使用されるインデックス。KEY が明示的に指定されていない場合、MySQL はクエリ条件に従って最適なインデックスを自動的に選択します。 key_len 実際に使用されるインデックスのバイト単位の長さ。インデックスが短いほど高速になり、一般にインデックス フィールドが小さいほど優れています。 参照 インデクス列等価問合せを使用する場合、インデクス列と等価一致するオブジェクト情報。定数相当クエリ const、func 使用時の式/関数、関連クエリ表示関連フィールド名 行 読み取る必要があるレコードの推定数。値が小さいほど優れています。つまり、結果セットが小さいほど、クエリの効率が高くなります。 フィルタリングされた テーブルが検索条件によってフィルタリングされた後に残っているレコード数の割合。値が小さいほど良好であり、インデックスを通じてデータを直接返すことができることを示します。 余分な 追加情報。インデックスまたはテーブル全体のスキャンがあるかどうかを確認します。通常、type フィールドで表示できます。インデックスの使用 (カバーインデックスが使用されます)、where の使用 (インデックスクエリは使用されません)、一時的な使用 (結果セットを一時テーブルに保存します。ソート/グループ化が使用されます)、ファイルソートの使用 (ソート操作はインデックスを使用しません)、結合バッファーの使用(接続条件が未使用のインデックス)、Impossible where (where 制約ステートメントに問題があり、結果セットが得られない可能性があります)
3. MySQL のチューニング
3.1 基本的な最適化
3.1.1 キャッシュの最適化
MySQL はバッファ プール サイズなどのパラメータを調整し、redis を導入します。ヒント: InnoDB はバッファ プールを使用してレコードとインデックスをキャッシュします
3.1.2 ハードウェアの最適化
サーバーにメモリー スティックを追加し、SSD ソリッド ステート ドライブをアップグレードし、ディスク I/O を複数のデバイスに分散し、マルチプロセッサを構成します。
3.1.3 パラメータの最適化
不要なサービスとログを閉じます。チューニング後に、低速クエリ ログを閉じます。
最大接続数を調整します: max_connections;
スレッド プール キャッシュ スレッドの数: thread_cache_size、アイドル状態のスレッドをキャッシュします。接続がある場合、接続を処理するためにスレッドを直接割り当てます。
バッファプールサイズ: innodb_buffer_pool_size。
3.1.4 ゴミを定期的に掃除する
使用されなくなったテーブル、データ、ログ、キャッシュなどは、MySQL リソースの占有が多すぎるのを避けるために適時にクリーンアップする必要があり、それによって MySQL のパフォーマンスが向上します。
3.1.5 適切なストレージ エンジンを使用する
MyISAM: 読み取りが頻繁で書き込みが少ないシナリオに適しています (テーブルレベルのロックと B+ リーフ ストレージ アドレスのため)
InnoDB: 同時書き込みシナリオに適しています (行レベルのロックのため、B+ はストア レコードを残します)。
InnoDB:外部キーとトランザクションをサポートし、行ロックは高い同時実行性に適しており、インデックスとデータをキャッシュし、メモリ要件が高く (インデックスとレコードをキャッシュする必要があるため)、大量のデータの保存に適しており、パフォーマンスが優れています。追加、削除、および変更 (行レベルのロックは同時実行性が高い)、ディスク集約型 (複数の非クラスター化インデックスがあるため、インデックスがレコード領域より大きくなる可能性があります)。
MyISAM:外部キーとトランザクションをサポートしません。テーブル ロックは高い同時実行性、キャッシュ インデックスとデータ アドレス、低いメモリ要件 (キャッシュ レコードがないため)、優れたクエリ パフォーマンス (InnoDB はクエリ時に MVCC の一貫性を維持する必要があるため) などには適していません。レコードをキャッシュします)、ディスクを節約します(ディスクには完全なレコードが保存されないため)。
比較した
InnoDB
マイISAM
特徴
外部キーとトランザクションのサポート
外部キーとトランザクションはサポートされていません
行テーブルのロック
行ロック。操作中に特定の行のみをロックし、他の行には影響を与えません。高度な同時操作に適しています。
テーブルロックは、1 つのレコードを操作してもテーブル全体がロックされるため、高度な同時操作には適していません。
キャッシュ
インデックスとデータのキャッシュには大量のメモリ要件があり、メモリ サイズがパフォーマンスに決定的な影響を与えます。
インデックスのみがキャッシュされ、実際のデータはキャッシュされません
フォーカスポイント
トランザクション: 同時書き込み、トランザクション、より大きなリソース
パフォーマンス: リソースの節約、消費量の削減、シンプルなビジネス、高速なクエリ
デフォルトで使用する
5.5以降 5.5 より前
3.1.6 読み取りと書き込みの分離
読み取りと書き込みの分離:読み取りと書き込みを分離すると、クエリのパフォーマンスが効果的に向上します。マスターとスレーブの同期には、bin ログとリレー ログが使用されます。
3.1.7 サブデータベースとサブテーブル
サブデータベースとサブテーブル:データ量が数千万件に達したら、縦分割(サブデータベース)、横分割(サブテーブル)、縦+横分割(サブデータベースとサブテーブル)。
コンセプト:
- 個別のテーブルのみ: 単一のテーブルには大量のデータがあり、読み取りと書き込みにボトルネックがあります。このテーブルが配置されているライブラリは、今後数年間の拡張にも対応できます。
- サブライブラリのみ: データベース全体の読み取りと書き込みがパフォーマンスのボトルネックとなり、ライブラリ全体が逆アセンブルされます。
- サブデータベースとサブテーブル: 1 つのテーブルに大量のデータがあり、データベースでもパフォーマンスのボトルネックが発生するため、サブデータベースとサブテーブルが必要です。
- 垂直に分割: フィールドを分割します。たとえば、spu テーブルの pic フィールドは非常に長いため、pic フィールドを別のテーブル (同じライブラリまたは異なるライブラリ) に分割することをお勧めします。
- 水平方向に分割: レコードを分割します。たとえば、テーブル内のデータ量が 100 万に達した場合、それを 200,000 個ずつ 4 つのテーブルに分割します。
分割原則:
データ量の増加 | データテーブルタイプ | 核となるアイデアを最適化する |
---|---|---|
データ量は数千万件と比較的安定したデータ量です | 状態テーブル | 分解できないなら分解して読まないで需要は横展開できる |
データの量は数千万、数十億以上に達する可能性があります | 流量計 | 事業分割、分散ストレージの設計 |
データの量は数千万、数十億以上に達する可能性があります | 流量計 | 設計データ統計要件ストレージの分散拡張 |
データ量は数千万、それほど多くないはずです | 構成テーブル | 小さくてシンプル、大きな統一は避ける |
サブデータベースサブテーブルの手順:
-
MySQL のチューニング:データ量は数千万で安定しており、数年以内に 1 億に達することはありません。実際、急いで解体する必要はありません。読み取りと書き込みを最適化するために、まず MySQL のチューニングを試してください。パフォーマンス。
-
目標評価:複数のライブラリとテーブルを評価および分解します。たとえば、現在の評価は 20 億、5 年後の評価は 100 億になります。複数のテーブルに分割しますか? 複数のライブラリに分割しますか? 答え: 合理的な答えです。1024 テーブル、16 ライブラリは 1024 テーブルとして計算され、200 万の単一テーブルが分割され、5 年後には 1000 万になります。1024 テーブル * 200w ≈ 100 億
-
テーブル分割:
-
事業層分割:混合事業から独立事業への分割、ホットとコールドの分離
-
データ層の分割:
-
日付による分割:この方法は、特に日付ディメンションによる分割の方が一般的です。実際、プログラム レベルでの変更はわずかですが、スケーラビリティの点で大きなメリットがあります。
- 日ディメンション分割(test_20191021 など)
- 月ディメンションの分割 (test_201910 など)
- 年ディメンションの分割 (test_2019 など)
-
主キーの範囲に従って分割します。たとえば、[1,200w] の主キーは 1 つのテーブルにあり、[200w, 400w] の主キーは 1 つのテーブルにあります。利点は、1 つのテーブル内のデータ量を制御できることです。欠点は、トラフィックを分散できず、書き込み操作が最後のテーブルに集中することです。
-
中間テーブル マッピング:テーブルを自由に分割し、中間テーブル レコード クエリのフィールド値と、それに対応するデータがどのテーブルにあるかを導入します。利点は柔軟性です。中間テーブルの導入によりプロセスが複雑になることが判明しました。
-
ハッシュ セグメンテーション: sharding_key%N。利点は、データの断片化が均一であり、トラフィックが分散されることです。欠点は、拡張にはデータの移行とノード間のクエリの問題が必要になることです。
-
パーティションごとに分割:ハッシュ、範囲など。データを水平方向に拡大縮小するのは実際には難しいため、お勧めしません。
-
-
-
Sharding_key (シャーディング テーブル フィールド) の選択:クエリ頻度が最も高いフィールドを選択し、テーブル分割方法に従ってフィールドを選択します。
-
コード変換:データベースがテーブルに分割された後の状況に適応するように、コード内のクエリおよび更新ステートメントを変更します。
-
データ移行:最も単純なものはダウンタイム移行であり、より複雑なものは非ダウンタイム移行であり、増分同期と完全同期を考慮する必要があります。
-
完全同期:古いデータベースから新しいデータベースへのデータ移行では、移行効率を制御し、増分データの一貫性を解決する必要があります。
- 時限タスク:古いライブラリをチェックし、新しいライブラリを書き込む時限タスク
- ミドルウェア:ミドルウェアを使用してデータを移行する
-
増分同期:古いライブラリから新しいライブラリへの移行中に、追加、削除、変更コマンドを誤ってライブラリに配置することはできません。
- 同期二重書き込み:新しいライブラリと古いライブラリを同期して書き込みます。
- 非同期二重書き込み (推奨):古いライブラリに書き込み、新しいライブラリに対して非同期でバイナリログを監視します。
- ミドルウェア同期ツール:特定のルールに従ってターゲット データベース テーブルにデータを同期します。
-
-
データの整合性検証と補正:非同期二重書き込み方式を採用していることを前提として、移行完了後、新旧のデータベースデータを一つずつ比較し、整合性があればスキップし、整合性がなければスキップします。 、彼らは補償されます:
- 新しいライブラリは存在しますが、古いライブラリは存在しません。新しいライブラリはデータを削除します。
- 新しいライブラリは存在しませんが、古いライブラリは存在します。新しいライブラリはデータを挿入します。
- 新しいライブラリが存在し、古いライブラリが存在します。すべてのフィールドを比較し、矛盾がある場合は、新しいライブラリを古いライブラリ データに更新します。
-
グレースケールのカット読み取り:グレースケールのリリースは、黒 (旧バージョン) と白 (新バージョン) の間を指します。ユーザーに意見がない場合は、一部のユーザーは古いバージョンを使い続け、一部のユーザーは新しいバージョンの使用を開始します。新しいバージョン、段階的にリリース すべてのユーザーはスムーズな移行リリースのために新しいバージョンに移行します。原則として:
- 問題がある場合は、時間内に古いライブラリに切り替えてください
- グレースケール ボリュームは最初は遅く、次に速くなり、各ボリュームは一定期間観察されます。
- 柔軟なルールをサポート: ディメンション グレースケール、100 (1,000) ポイント比率グレースケールを保存
-
古いものを停止して新しいものを使用します。古いライブラリをオフラインにして、新しいライブラリで読み取りと書き込みを行います。
3.2 テーブル設計の最適化
3.2.1 混合ビジネスサブテーブル、ホットデータサブテーブルとコールドデータサブテーブル
たとえば、大きなタスク テーブルをタスク テーブルと履歴タスク テーブルに分割し、タスク テーブル内のタスクが完了すると、履歴タスク テーブルに移動します。タスク テーブルはホット データであり、履歴タスク テーブルはコールド データであるため、クエリのパフォーマンスが向上します。
3.2.2 結合クエリを中間リレーショナルテーブルに変更する
たとえば、属性テーブルと属性グループ テーブルは結合クエリを使用せず、「属性 - 属性グループ テーブル」を使用して各属性の ID と「属性関係」を保存します。
3.2.3 3 つのパラダイムに従う
各属性は細分化できません。テーブルには主キーが 1 つだけ必要です。また、主キー以外の列は主キーに直接依存する必要があります。
3.2.4 フィールド提案の非 null 制約
① クエリにヌルポインタの問題がある可能性があります。
②集計関数はnullを無視するため不正確です
③ 「=」を使用して判断することはできません。判断するには is null のみを使用できます。
④ Null およびその他の値の操作は null のみであるため、誤って 0 として扱う可能性があります。
⑤ NULL 値は NULL 文字より多くのスペースを占め、NULL 値の長さは 0、NULL 値の長さは 1 ビットです。
⑥インデックスをカバーしていない場合、nullでない場合はインデックスを使用できません
3.2.5 冗長フィールドの使用
列フィールドは多すぎることはできませんが、クエリ効率を高めるために冗長フィールドを追加できます。
3.2.6 データ型の最適化
整数型:
値の範囲を考慮し、初期の安定性を確保するために int を使用します。非負の型では UNSIGNED を使用する必要があります。同じバイト数でより広い範囲の値を格納できます。主キーは通常bigint、ブール型tinintを使用します。
整数を使用できる場合は、テキスト タイプを使用しないでください。
大きな整数は、テキスト データよりも使用するストレージ スペースが少なくなる傾向があります。
TEXT、BLOB データ クラスの使用は避けてください。
これら 2 つの大きなデータ型の場合、一時メモリ テーブルを並べ替えに使用することはできず、ディスク一時テーブルのみを使用できます。効率が非常に悪いため、これらを使用しないか、テーブルを別の拡張テーブルに分割することをお勧めします。LongBlob タイプは 4G ファイルを保存できます。
列挙型の使用は避けてください。
並べ替えが遅いです。
TIMESTAMP を使用して時刻を保存します。
TIMESTAMP は 4 バイト、DATETIME は 8 バイトを使用し、TIMESTAMP は自動割り当てと自動更新の特性を持っています。欠点は、2038 年までしか保存できないことです。MySQL5.6.4 バージョンはパラメータ化でき、自動的に BIGINT 型に変更されます。
DECIMAL は浮動小数点数を格納します。
Decimal 型は正確な浮動小数点数であり、特に財務関連の財務データの場合、計算中に精度が失われることはありません。占有スペースは定義された幅によって決まり、4 バイトごとに 9 桁を格納でき、小数点は 1 バイトを占めます。bigint より大きい整数データを格納するために使用できます。
3.3 インデックスの最適化
3.3.1 インデックス障害を考慮した 11 のシナリオ
詳細については、以下を参照してください。
すべての値を一致させてみます。
age、classId、name をクエリする場合、(age,classId,name) インデックスは (age,classId) より高速です。
一番左のプレフィックスを考えてみましょう。
結合インデックスでは、頻繁にクエリされる列が左側に配置されます。インデックス (a、b、c) は、(a、b、c)、(a、b)、(a) のみを検索できます。
主キーは可能な限り順序付けする必要があります。
主キーが間違っている場合は、ターゲットの場所を見つけて挿入する必要があります。また、ターゲットの場所が存在するデータ ページがいっぱいの場合は、ページにスコアを付ける必要があり、パフォーマンスが低下します。自動インクリメント戦略または MySQL8.0 順序付け UUID 戦略を選択できます。
計算と関数はインデックスの失敗につながります。
num+1=2 などの計算、abs(num) などの関数は絶対値を取る
型変換によりインデックスが無効になります。
たとえば、name='123' ではなく name=123 になります。別の例は、異なる文字セットの使用です。
範囲条件の右側の列インデックスが無効です。
たとえば、(a, b, c) ジョイント インデックス、クエリ条件 a、b、c、b が範囲クエリを使用する場合、b の右側の c インデックスは無効になります。範囲クエリが必要なフィールドは最後に置くことをお勧めします。範囲には、(<) (<=) (>) (>=) およびその間が含まれます。
カバーするインデックスがない場合、「等しくない」はインデックスを無効にします。
「等しくない」は正確に照合できないため、セカンダリ インデックス ツリーをフル テーブル スキャンしてからテーブルに戻る効率は、クラスタ化インデックス ツリーの直接フル テーブル スキャンほど良くありません。ただし、カバーリングインデックスを使用する場合、結合インデックスのデータ量は小さく、クラスタ化インデックスツリーよりもメモリにロードするのに必要な領域が小さく、テーブルに返す必要がありません。インデックス作成の効率は、フル テーブル スキャンのクラスター化インデックス ツリーよりも優れています。
カバリングインデックス:クエリ結果を満たすデータを含むインデックスをカバリングインデックスと呼び、テーブルに返すなどの操作が必要ありません。
インデックスがカバーされていない場合、左ファジー クエリによりインデックスが失敗します。
たとえば、「%abc」のようにします。文字列の先頭を正確に一致させることができないためです。上記と同じ理由です。
インデックスがカバーされていない、null でない、などの場合はインデックスを使用できません。
正確に一致させることはできないからです。上記と同じ理由です。
「OR」の前後にインデックスのない列があるため、インデックスが失敗します。
MySQL では、 または の左側の条件が満たされていても、右側の条件を判断する必要があります。
異なる文字セットではインデックス作成が失敗します。
utf8mb4 を使用することをお勧めします。比較する前に異なる文字セットを変換する必要があるため、インデックスが失敗します。
3.3.2 インデックス設計原則に従う
詳細については、以下を参照してください。
- 命名: インデックスフィールドの数は 5 を超えてはならず、命名形式は「idx_col1_col2」です。
- 頻繁にクエリされる列 (特にグループ化、範囲、並べ替えクエリ) にインデックスを構築します。
- 頻繁に更新されるテーブルの場合は、インデックスを作成しすぎないでください。
- 固有の特性を持つフィールドはインデックスの作成に適しています。
- 非常に長い varchar フィールド。識別と長さに基づいてプレフィックス インデックスを作成するのに適しています。
- 複数のフィールドにインデックスを付ける必要がある場合、結合インデックスは単一値インデックスよりも優れています。
- インデックスの作成が多すぎることを避け、インデックスの失敗を避けてください。
- 順序付けされたフィールドを主キー インデックスとして使用するようにしてください。順序が正しくない場合に新しい主キーが完全なデータ ページに移動されず、挿入後にデータ ページが分割され、パフォーマンスが低下することを防ぎます。
3.3.3 接続クエリの最適化
詳細については、以下を参照してください。
外部結合中は、ドリブン テーブル接続フィールドのインデックス付けが優先されます。
外部結合クエリを実行する場合、右側のテーブルが駆動テーブルとなるため、インデックスを追加することをお勧めします。左側のテーブルはすべてのデータをチェックし、右側のテーブルは条件によって検索するため、右側のテーブルの条件フィールドにインデックスを作成する価値は少し高くなります。
内部結合の場合、オプティマイザはインデックスなしでインデックス テーブルを自動的に駆動し、大きなテーブルを小さなテーブルで駆動します。
まず、インデックスを持つテーブルを駆動テーブルとして選択します。どちらのテーブルにもインデックスがない場合、クエリ オプティマイザーは自動的に小さなテーブルが大きなテーブルを駆動するようにします。ドリブン テーブルの JOIN フィールドにインデックスを作成すると、クエリの効率が大幅に向上します。
2 つのテーブルの接続フィールドは同じタイプである必要があります。
2 つのテーブルの JOIN フィールドのデータ型は完全に一致しています。自動型変換によってインデックスが無効になるのを防ぎます。
3.3.4 サブクエリの最適化
詳細については、以下を参照してください。
サブクエリではなく関連付け:複数のテーブルとの直接の関連付けは、サブクエリを使用せずにできるだけ直接的に行う必要があります。(クエリの回数を減らします)。サブクエリは、別の SELECT ステートメントの条件として使用される 1 つの SELECT クエリの結果です。
#取所有不为班长的同学
SELECT a.* FROM student a WHERE a.stuno NOT IN (
SELECT monitor FROM class b
WHERE monitor IS NOT NULL
);
#优化成关联查询
SELECT a.* FROM student a LEFT OUTER JOIN class b
ON a.stuno = b.monitor WHERE b.monitor IS NULL;
サブクエリの代わりに複数のクエリ:サブクエリの使用は推奨されません。サブクエリ SQL を逆アセンブルしてプログラムを複数のクエリと組み合わせるか、サブクエリの代わりに JOIN を使用することをお勧めします。
3.3.5 ソートの最適化
詳細については、以下を参照してください。
- オプティマイザはソート方法を自動的に選択します: MySQL はインデックス ソートと FileSort ソートをサポートしており、インデックスはレコードの順序を保証し、パフォーマンスが高いため、使用することをお勧めします。FileSortのソートはメモリ内ソートであり、データ量が多い場合にはディスク上に一時ファイルを生成してソートするため、効率が悪くCPUを多く消費します。FileSort が必ずしも非効率であるというわけではありません。場合によっては効率的である可能性があります。たとえば、左ファジー、「等しくない」、null ではない、およびインデックスをカバーしないその他のインデックス障害の場合、テーブル全体のスキャンの効率は、非クラスター化インデックスのツリー トラバーサルおよびテーブル リターンの効率よりも高くなります。
- 左端のプレフィックスを満たすには:条件とフィールドによる順序で結合インデックスが作成される場合、順序は左端のプレフィックスを満たす必要があります。たとえば、インデックス (a,b,c)、a=1 のクエリを b,c で並べます。
- すべて昇順またはすべて降順:並べ替え順序はすべて DESC またはすべて ASC である必要があります。順序が崩れるとインデックスが失敗します。
- ソート対象の数が多い場合、インデックスは無効ではありませんが、インデックス効率はファイルソートほど良くありません。ソート対象のデータ量が約 10,000 を超えるため、ファイルソートはインデックス付けに使用されません。データ量を削減するには、制限とフィルタリングの場所を使用することをお勧めします。データ量が大きい場合、インデックスをソートした後にテーブルに戻ってすべてのデータを確認する必要があり、パフォーマンスが非常に悪く、メモリ内でソートする場合は FileSort ほど効率的ではありません。制限を使用すると必ずインデックス ソートが使用されるというわけではありません。重要なのはデータの量です。データの量が大きすぎる場合、オプティマイザーは FileSort を使用してソートします。
- 範囲クエリの右側の並べ替えインデックスが無効です。たとえば、インデックス (a,b,c)、a>1 のクエリを b,c で並べると、b,c 並べ替えではインデックスを使用できなくなり、ファイルソートになります。が必要です。
- 大量の範囲クエリ フィルタリングがある場合は、範囲フィールドにインデックスを追加することが優先されます。 [範囲条件] フィールドと [グループ化基準または並べ替え基準] フィールドがオプションであるように見える場合、フィルタリングされたデータが十分にあるのに十分ではない場合ソートするデータが多い場合は、範囲フィールドにインデックスを付けることが優先されます。このように、範囲クエリによって並べ替えインデックスが失敗した場合でも、並べ替えフィールドのみにインデックスが作成される場合よりも効率は高くなります。少ししかフィルタリングできない場合は、最初に並べ替えフィールドにインデックスを設定します。
- FileSort のチューニング:インデックス ソートが使用できない場合は、FileSort メソッドをチューニングする必要があります。たとえば、sort_buffer_size (ソート バッファ サイズ) と max_length_for_sort_data (ソートされたデータの最大長) を増やします。
3.3.6 グループの最適化
基本的には並べ替えと同じ考え方です。
並べ替えとグループ化はより多くの CPU を消費するため、できる限り使用しないでください。
持つよりも効率的な場所。ここで、 はグループ化前のフィルタリング、having はグループ化後のフィルタリングです。
3.3.7 ディープページングクエリの最適化
要件は、2000000 ~ 2000010 番目のレコードを返すことです。
順序付けされた主キーを持つテーブルは、主キーに従って並べ替えられ、最初にフィルター処理されてから並べ替えられます。範囲の後のデータを直接確認します。
EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;
順序付けされていない主キーを持つテーブルは主キーに従って並べ替えられ、最初に主キーをページングしてから元のテーブルに接続します。現在のテーブルは並べ替えとインターセプト後に主キー テーブルに接続され、接続フィールドが主キーになります。 。主キーはクラスター化インデックス ツリーでチェックされるため、テーブルに戻る必要がなく、ソートとページングが非常に高速です。
EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a WHERE t.id = a.id;
順序付けされた主キーを持つテーブルは、非主キーに従って並べ替えられます。つまり、前のページの最後のレコード x を取得すると、ターゲット ページ番号のすべてのレコード ID が x.id より小さくなります (順序が逆であり、並べ替えの基準が次のとおりであるため)。実際には age、id、主キーは自己インクリメントです)、ターゲット ページ番号のすべてのレコードの経過時間が x.age 以下です。
EXPLAIN SELECT * FROM student WHERE id<#{x.id} AND age>=#{x.age} ORDER BY age DESC LIMIT 10;
3.3.8 インデックスをカバーするように努める
詳細については、以下を参照してください。
MySQL Advanced - インデックス、プレフィックス インデックス、インデックス プッシュダウン、SQL 最適化、主キー設計をカバーする_vincewm のブログ-CSDN ブログ
インデックスには、クエリ結果を満たすデータが含まれます。テーブルに戻る必要がないため、クエリ効率が高くなります。インデックスをカバーする場合、「左あいまい」と「等しくない」はインデックスを無効にすることはできません。
例:
#没覆盖索引的情况下,左模糊查询导致索引失效
CREATE INDEX idx_age_name ON student(age, NAME);
EXPLAIN SELECT * FROM student WHERE NAME LIKE '%abc';
カバリングインデックス:クエリ結果を満たすデータを含むインデックスをカバリングインデックスと呼び、テーブルに返すなどの操作が必要ありません。
インデックスは行を効率的に検索する方法の 1 つですが、一般にデータベースはインデックスを使用して列のデータを検索することもできるため、行全体を読み取る必要はありません。結局のところ、インデックス リーフ ノードにはインデックス付けされたデータが格納され、インデックスを読み取ることで目的のデータが取得できる場合は、行を読み取る必要はありません。
カバー インデックスは非クラスター化インデックスの形式で、クエリ内の SELECT、JOIN、および WHERE 句で使用されるすべての列が含まれます (つまり、インデックス付きフィールドは、カバーされるクエリ条件に関係するフィールドとまったく同じです)。簡単に言えば、インデックス列 + 主キーには、SELECT と FROM の間でクエリされた列が含まれます。
3.3.9 文字列プレフィックスインデックス
たとえば (email(6))、文字列全体ではなく文字列プレフィックスにインデックスを追加します。プレフィックスの長さは識別の程度と長さに応じて選択する必要があります。
例:
MySQL はプレフィックス インデックスをサポートしています。デフォルトでは、プレフィックスの長さを指定せずにインデックスを作成すると、インデックスには文字列全体が含まれます。
mysql> alter table teacher add index index1(email);
#或
mysql> alter table teacher add index index2(email(6));
データ構造とストレージの点で、これら 2 つの異なる定義の違いは何ですか? 以下の図は、これら 2 つの指標の模式図です。
Index1 が使用される場合(インデックスには文字列全体が含まれます)、実行順序は次のようになります。
- Index1 のインデックス ツリーから「[email protected]」のインデックス値を満たすレコードを検索し、ID2 の値を取得します。
- テーブルに戻って、主キーの値が ID2 である行を主キーで見つけ、電子メールの値が正しいと判断し、この行レコードを結果セットに追加します。
- Index1 のインデックス ツリー上で見つかった位置にある次のレコードを取得すると、email='[email protected]' の条件が満たされなくなっていることがわかり、ループが終了します。
このプロセスでは、主キー インデックスからデータを 1 回取得するだけでよいため、システムは 1 行のみがスキャンされたとみなします。
Index2 が使用される場合(インデックスには文字列プレフィックス email(6) が含まれます)、実行シーケンスは次のようになります。
- Index2 インデックス ツリーから 'zhangs' のインデックス値を満たすレコードを検索します。最初に見つかったレコードは ID1 です。
- テーブルに戻り、主キーの主キー値が ID1 である行を見つけ、電子メールの値が「 [email protected] 」ではないと判断し、この行のレコードを破棄します。
- インデックス 2 で見つかった位置にある次のレコードを取得し、それがまだ「zhangs」であることを確認し、ID2 を取り出して、テーブルに戻ってID インデックスの行全体をフェッチし、値が正しいと判断します。今回は、この行を結果セットに追加します。
- Index2 で取得した値が 'zhangs' でなくなるまで、前の手順を繰り返します。ループは終了します。
つまり、プレフィックス インデックスを使用して長さを定義すると、余分なクエリ コストを追加することなくスペースを節約できます。識別度については前述しましたが、識別度が高ければ高いほど良いです。識別の度合いが高いほど、重複キーの値が少なくなるからです。
3.3.10 MySQL5.6でサポートされているインデックスプッシュダウンを使ってみる
結合インデックスのフィールドがファジー クエリ (非左ファジー) の場合、フィールドの判定後、次のいくつかのフィールドを直接判定できます。判定がフィルタリングされた後、テーブルに戻ってフィールドの条件を判定します。ジョイントインデックスには含まれていません。
たとえば、インデックス (名前、年齢)、「z%」などのクエリ名、年齢と住所、ファジー クエリでは年齢が不規則になります。
結合インデックスツリーを問い合わせる際には、名前だけでなくその後の年齢も判断し、フィルタリング後テーブルに戻って住所を判断します。また、インデックス プッシュダウンがオフになっている場合、ジョイント インデックス内のファジー クエリ (非左) の次のフィールドはジョイント インデックス ツリーで直接判断できず、テーブルに戻ってから判断する必要があります。
詳細な説明:
インデックス条件プッシュダウン(ICP、インデックス条件プッシュダウン) は MySQL 5.6 の新機能で、インデックスを使用してストレージ エンジン層でデータをフィルタリングするための最適化された方法です。
- ICPがない場合:結合インデックスのフィールドがあいまいクエリ(非左あいまい)の場合、フィールド判定後、以下のフィールドは直接条件判定に使用できず、必ず戻ってから判定する必要があります。テーブル。
- ICP が有効になった後: 結合インデックス内のフィールドがファジー クエリ (左ファジーではない) の場合、フィールドが判定された後、次のいくつかのフィールドを直接判定できます。判定がフィルタリングされた後、テーブルに戻ってクエリを確認します。フィールドの条件は共同インデックスジャッジに含まれていません。主な最適化ポイントは、テーブルに戻る前にフィルタリングして、テーブルに戻る回数を減らすことです。主な用途:ファジィクエリ(非左ファジィ)では、インデックス内のフィールドの後ろのフィールドの順序が狂うため、テーブルに戻って判定する必要がありますが、インデックスプッシュダウンを使用する場合は、リターンする必要はありません。テーブルに反映され、判断はジョイント インデックス ツリーで直接行われます。
ICP がない場合、ストレージ エンジンはインデックスを走査してベース テーブル内の行を見つけ、それらを MySQL サーバーに返します。MySQL サーバーは WHERE の背後にある条件が予約されているかどうかを評価します。
ICP が有効になった後、インデックス内の列のみを使用して WHERE 条件の一部をフィルタリングできる場合、MySQL サーバーは WHERE 条件のこの部分をストレージ エンジン フィルタに入れます。次に、ストレージ エンジンはインデックス エントリを使用してデータをフィルタリングし、この条件が満たされる場合にのみテーブルから行を読み取ります。利点: ICP により、ストレージ エンジンがベース テーブルにアクセスする必要がある回数と、MySQL サーバーがストレージ エンジンにアクセスする必要がある回数が削減されます。ただし、ICP の高速化効果は、ストレージ エンジン内で ICP によってフィルタリングされたデータの割合に依存します。
例:
インデックス プッシュダウンをサポートしないジョイント インデックス:たとえば、インデックス (名前、年齢)、「z%」などのクエリ名、年齢=? 、ファジークエリにより年齢の順序が狂います。結合インデックスツリーをクエリする場合、名前のみが検索され、その後の年齢は条件によって直接判断できず、テーブルに戻ってから年齢を判断する必要があります。
インデックス プッシュダウンをサポートするジョイント インデックス:たとえば、インデックス (名前、年齢)、「z%」などのクエリ名、年齢とアドレス。ジョイント インデックス ツリーをクエリするときに名前をチェックするだけでなく、その後の年齢も判断します。フィルターしてテーブル判定アドレスを返します。
CREATE INDEX idx_name_age ON student(name,age);
#索引失败;非覆盖索引时,左模糊导致索引失效
EXPLAIN SELECT * FROM student WHERE name like '%bc%' AND age=30;
#索引成功;MySQL5.6引入索引下推,where后面的name和age都在联合索引里,可以又过滤又索引,不用回表,索引生效
EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30;
#索引成功;name走索引,age用到索引下推过滤,classid不在联合索引里,需要回表。
EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30 AND classid=2;
利点: 一部のシナリオでは、ICP はテーブルの戻り数を大幅に削減し、パフォーマンスを向上させることができます。ICP を使用すると、ストレージ エンジンがベース テーブルにアクセスする必要がある回数と、MySQL サーバーがストレージ エンジンにアクセスする必要がある回数を減らすことができます。ただし、ICP の高速化効果は、ストレージ エンジン内で ICP によってフィルタリングされたデータの割合に依存します。
3.3.11 より多くの書き込みが必要で、読み取りが少なくなるシナリオでは、通常のインデックスを使用してみてください。
クエリ実行時の共通インデックスと一意インデックスの効率はほぼ同じですが、更新時には、更新されたデータ ページをメモリにキャッシュする変更バッファ (書き込みキャッシュ) があり、マージ操作が高速になるため、共通インデックスの効率が高くなります。次回の訪問時またはバックグラウンドで定期的に実行され、データ ページはディスクに書き込まれます。
データの永続性を確保するために、トランザクションがコミットされると変更バッファが REDO ログに書き込まれます。通常のインデックス: Student(name) にインデックス idx_name を作成するなどの制限はありません。一意のインデックス: UNIQUE パラメータは、student(name) に UNIQUE インデックス idx_name を作成するなど、インデックスが一意であるように制限します。
詳細な説明:
書き込みキャッシュ (変更バッファ):
データ ページを更新する必要がある場合、データ ページがメモリ内にある場合は直接更新され、データ ページがメモリ内にない場合は、 InooDB はデータの整合性に影響を与えることなく。このデータ ページをディスクから読み取る必要はありません。次のクエリでこのデータ ページにアクセスする必要がある場合、データ ページをメモリに読み取り、変更バッファ内のこのページに関連する操作を実行します。このようにして、データ ロジックの正確性を保証できます。
マージ:変更バッファ内の操作を元のデータ ページに適用して最新の結果を取得するプロセスは、マージと呼ばれます。このデータ ページにアクセスするとマージがトリガーされるだけでなく、システムには定期的にマージするバックグラウンド スレッドがあります。マージ操作は、通常のデータベースのシャットダウン時にも実行されます。
更新操作を最初に変更バッファーに記録してディスク読み取りを減らすことができれば、ステートメントの実行速度が大幅に向上します。さらに、データをメモリに読み込むにはバッファ プールが必要となるため、この方法ではメモリの占有を回避し、メモリ使用率を向上させることもできます。
一意のインデックスの更新には変更バッファを使用できません。実際には、通常のインデックスのみが使用できます。
区別してください:
- バッファ プール バッファ プールを使用してデータを読み取ります。
- REDOログにはREDOログ・バッファがあり、バッファ・プール内の更新データをREDOログ・バッファに書き込みます。トランザクションがコミットされると、REDOログ・バッファはフラッシュに従ってREDOログ・ファイルまたはページ・キャッシュにフラッシュされます。戦略。
3.4 SQLの最適化
詳細については、以下を参照してください。
MySQL Advanced - インデックス、プレフィックス インデックス、インデックス プッシュダウン、SQL 最適化、主キー設計をカバーする_vincewm のブログ-CSDN ブログ
EXISTS と IN の合理的な選択:
小さなテーブルが大きなテーブルを駆動するという原則に従い、左側の小さなテーブルは EXISTS で、左側の大きなテーブルは IN を使用します。
COUNT(1) または COUNT(*) を試してください。
innoDB の場合、COUNT(1) および COUNT(*) の場合、クエリ オプティマイザーは、非クラスター化インデックス ツリーが見つからない場合にのみ、インデックスと統計用のスペースが最も小さいセカンダリ インデックス ツリーの選択を優先します。 、クラスター化インデックスが使用されます。ツリー統計は多くのスペースを占有します。もちろん、COUNT (最小スペース副インデックスフィールド) を使用することもできますが、オプティマイザによる自動選択ほど手間はかかりません。MyISAM を使用する場合、どの時間計算量が O(1) であるかは問題ではありません。
SELECT (明示的なフィールド) を試してください。
フィールドを指定することをお勧めします。クエリ オプティマイザーはすべての列名の "*" 記号を解析するのに時間がかかり、"*" 記号はカバー インデックスを使用できません。
テーブル全体をスキャンする場合は、「LIMIT」を使用してみてください。
テーブル全体がスキャンされ、結果セット内のレコード数がわかっている場合は、limit を使用してそれを制限し、十分なスキャン後に停止し、テーブル全体をスキャンしなくなるようにします。インデックスがある場合は、制限を使用する必要はありません。
制限 N を使用し、制限 M、N を少なく使用します:
特にテーブルやMが比較的大きい場合。
長いトランザクションを複数の小さなトランザクションに分割します。
可能な限り COMMIT を使用し、宣言的トランザクションの代わりにプログラムによるトランザクションを使用し、トランザクションの粒度を減らします。トランザクションのコミットによって解放できるリソース: データ、ロック、REDO/UNDO ログ バッファ内の領域を復元するために使用されるロールバック セグメントに関する情報。
まず確認してから削除します。
UPDATE、DELETE ステートメントには明確な WHERE 条件が必要です。
UNION の代わりに UNION ALL を試してください。
UNION ALL は重複排除を行わず、より高速です。