MySQL の遅いクエリの落とし穴

MySQL の遅いクエリの落とし穴

クエリが遅いとどのような結果が生じますか?若い頃は、データを返すのが遅くなり、ユーザー エクスペリエンスが悪くなるだけだといつも思っていました。実際は、それ以上です。私はオンライン事故を何度か経験しましたが、そのうちの 1 つは SQL クエリの遅さが原因でした。

クエリSQLだったと記憶しています。データ量が数万のときでも、0.2秒以内でした。時間が経つにつれてデータが急増し、消費時間は2〜3秒に達したこともありました。インデックスはヒットせず、テーブル全体がスキャンされます。 explain の extra には、where の使用、temporary の使用、filesort の使用が示されています。一時テーブル ソートを使用する必要があります。高頻度のクエリにより、DB スレッド プールはすぐに同時実行でいっぱいになり、大量のクエリ要求が蓄積されます。DB サーバーの CPU は長時間 100% 以上になり、大量の要求がタイムアウトします。 。結局、システムはクラッシュしました。ボスが来ましたよ〜

ちなみに、10月2日の午後8時半。私は故郷の棗荘で、仲間たちと屋台に座って自慢していました!私がどんな恥ずかしい状況に直面すると思いますか?

チームが遅いクエリに十分な注意を払わないと、リスクが非常に高くなることがわかります。その事故の後、上司はこう言いました。「誰かのコードで同様の事故が再び起こったら、開発と部門リーダーは一緒に解雇される」。多くのリーダーがこれに怯え、急いで DBA の同僚を 2 人雇いました 🙂🙂🙂

スロークエリは、その名前が示すように、実行速度が非常に遅いクエリです。どれくらい遅いですか? long_query_time パラメータで設定された時間しきい値 (デフォルトは 10 秒) を超えると、遅いとみなされ、最適化が必要になります。遅いクエリは、遅いクエリ ログに記録されます。

スロー クエリ ログは、デフォルトでは有効になっていません。SQL ステートメントを最適化する必要がある場合は、この機能を有効にできます。これにより、どのステートメントを最適化する必要があるかを簡単に知ることができます (SQL ステートメントに 10 秒かかるとしたら、どれほどひどいことか考えてみてください)。

マーフィーの法則: 起こりうる問題は必ず起こる。

それはあまりにも現実的なことの一つです。問題が発生する前に防止するために、遅いクエリを処理する方法を見てみましょう。この記事は非常に退屈なので、コップ一杯の水を飲むのを忘れないでください。読む時間がない場合は、まず保存してください。

1. 遅いクエリ構成

1-1. スロークエリを有効にする

MySQLサポート

  • 1. スロー クエリを(一時的に)有効にするコマンドを入力します。MySQL サービスの再起動後、スロー クエリは自動的に無効になります。
  • 2. my.cnf (Windows の場合は my.ini) システム ファイルを設定して有効にします。設定ファイルを変更すると、低速クエリを永続的に有効にすることができます。

方法1: コマンドでスロークエリを有効にする

ステップ 1. slow_query_log をクエリして、スロー クエリ ログが有効になっているかどうかを確認します。

'%slow_query_log%' のような変数を表示します。
mysql> '%slow_query_log%' のような変数を表示します。
+---------------------+-----------------------------------+
| 変数名 | 値 |
+---------------------+-----------------------------------+
| slow_query_log | オフ |
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+
セットに2行(0.01秒)

ステップ 2: スロー クエリ コマンドを有効にします。

グローバル slow_query_log を 'ON' に設定します。

ステップ 3: スロー クエリ ログの SQL 実行時間を記録するためのしきい値を指定します (long_query_time 単位: 秒、デフォルトは 10 秒)

以下のように、1秒に設定しました。実行に1秒以上かかるSQL文はスロークエリログに記録されます。

グローバル long_query_time を 1 に設定します。

ステップ 4: 「スロー クエリ ログ ファイルの保存場所」をクエリする

'%slow_query_log_file%' のような変数を表示します。
mysql> '%slow_query_log_file%' のような変数を表示します。
+---------------------+-----------------------------------+
| 変数名 | 値 |
+---------------------+-----------------------------------+
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+---------------------+-----------------------------------+
セット内の1行(0.01秒)

slow_query_log_file は、スロークエリログの保存パスとファイルを指定します(デフォルトではデータファイルと一緒に配置されます)。

ステップ5: スロークエリが有効になっていることを確認する

更新するには、現在の MySQL ターミナルを終了して再度ログインする必要があります。

スロークエリが設定されると、条件を満たす次の SQL ステートメントが記録されます。

  • クエリステートメント
  • データ変更ステートメント
  • ロールバックされたSQL

方法2: my.cnf (Windowsの場合はmy.ini)システムファイルを設定して有効にする

(バージョン: MySQL 5.5 以上)

低速クエリを有効にするには、my.cnf ファイルの [mysqld] の下に次の設定を追加します。

# スロークエリ機能を有効にする slow_query_log=ON
# スロークエリログSQL実行時間を記録するしきい値を指定します long_query_time=1
# オプション、デフォルトのデータファイルパス # slow_query_log_file=/var/lib/mysql/localhost-slow.log

データベースを再起動すると、スロークエリが永続的に有効になります。クエリの検証は次のとおりです。

mysql> '%_query_%' のような変数を表示します。
+--------------------------------+-----------------------------------+
| 変数名 | 値 |
+--------------------------------+-----------------------------------+
| クエリキャッシュがある | はい |
| 長いクエリ時間 | 1.000000 |
| slow_query_log | オン |
| slow_query_log_file | /var/lib/mysql/localhost-slow.log |
+--------------------------------+-----------------------------------+
セット内の6行(0.01秒)

1-2. スロークエリログの概要

上記のように、これは実行に1秒以上かかるSQL文です(テスト)

  • 最初の行: 記録的な時間
  • 2行目: ユーザー名、ユーザーIP情報、スレッドID番号
  • 3行目: 実行時間[単位:秒]、ロック取得の実行時間、取得された結果行数、スキャンされたデータ行数
  • 4行目: このSQLが実行された時のタイムスタンプ
  • 5行目: 特定のSQL文

2. 遅いクエリSQLの分析を説明する

MySQL のスロー クエリ ログを分析します。explain キーワードを使用してオプティマイザーをシミュレートし、SQL クエリ ステートメントを実行して、SQL のスロー クエリ ステートメントを分析します。以下のテスト テーブルは、137 万件のデータを含むアプリ情報テーブルです。これを例として分析してみましょう。

SQL の例は次のとおりです。

-- 1.185秒
SELECT * from vio_basic_domain_info where app_name like '%陈哈哈%';

これは一般的なあいまいクエリ ステートメントです。クエリには 1.185 秒かかり、148 件のレコードが見つかりました。
Explain 分析の結果は次の表に示されています。テーブル情報によると、SQL ステートメントは app_name フィールドのインデックスを使用しておらず、クエリ タイプはフル テーブル スキャンであり、スキャンされた行数は 1.37w です。

mysql> EXPLAIN SELECT * from vio_basic_domain_info where app_name like '%陈哈哈%';
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+----------+----------+----------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+----------+----------+----------+
| 1 | SIMPLE | vio_basic_domain_info | NULL | ALL | NULL | NULL | NULL | NULL | 1377809 | 11.11 | where の使用 |
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+----------+----------+----------+
セットに 1 行、警告 1 件 (0.00 秒)

このSQLがインデックスを使用する場合、SQLは次のようになります。クエリ時間: 0.156秒、141件のデータが見つかりました

-- 0.156秒
SELECT * from vio_basic_domain_info where app_name like '陈哈哈%';

Explain 分析結果は次のとおりです。テーブル情報によると、SQL は idx_app_name インデックスを使用し、クエリ タイプはインデックス範囲クエリであり、スキャンされた行数は 141 です。クエリ内のすべての列がインデックス (select *) に含まれているわけではないため、他の列のデータを取得するためにテーブルが一度返されます。

mysql> EXPLAIN SELECT * from vio_basic_domain_info where app_name like '陈哈哈%';
+----+-------------+-----------------------+------------+--------+---------------+--------------+-------+-------+---------+------------------------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+--------+---------------+--------------+-------+-------+---------+------------------------+
| 1 | SIMPLE | vio_basic_domain_info | NULL | 範囲 | idx_app_name | idx_app_name | 515 | NULL | 141 | 100.00 | インデックス条件を使用 |
+----+-------------+-----------------------+------------+--------+---------------+--------------+-------+-------+---------+------------------------+
セットに 1 行、警告 1 件 (0.00 秒)

このSQLがカバーインデックスを使用する場合、SQLは次のようになります。クエリ時間: 0.091秒、141件のデータが見つかりました

-- 0.091秒
SELECT app_name from vio_basic_domain_info where app_name like '陈哈哈%';

Explain 分析結果は以下のとおりです。テーブル情報によると、上記の SQL と同様にインデックスが使用されています。クエリ列がインデックス列に含まれているため、テーブルを返す時間が 0.06 秒短縮されています。

mysql> EXPLAIN SELECT app_name from vio_basic_domain_info where app_name like '陈哈哈%';
+----+-------------+------------------------+------------+--------+---------------+--------------+-------+-------+---------+---------------------------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+------------------------+------------+--------+---------------+--------------+-------+-------+---------+---------------------------+
| 1 | SIMPLE | vio_basic_domain_info | NULL | range | idx_app_name | idx_app_name | 515 | NULL | 141 | 100.00 | where の使用; index の使用 |
+----+-------------+------------------------+------------+--------+---------------+--------------+-------+-------+---------+---------------------------+
セットに 1 行、警告 1 件 (0.00 秒)

では、EXPLAIN 解析結果を通じて SQL をどのように分析するのでしょうか?各列の属性は何を表していますか?一緒に下を見ましょう。

2-1. 各列属性の紹介:

  • id: SELECT クエリのシーケンス番号。実行の優先順位を反映します。サブクエリの場合は、id シーケンス番号が増加します。id 値が大きいほど優先順位が高くなり、早く実行されます。
  • select_type: クエリのタイプを示します。
  • table: 出力結果セットのテーブル。エイリアスが設定されている場合は、それも表示されます。
  • パーティション: 一致するパーティション
  • タイプ: テーブルへのアクセス方法
  • possible_keys: クエリ時に使用できるインデックスを示します
  • キー: 実際に使用されるインデックスを示します
  • key_len: インデックスフィールドの長さ
  • ref: 列とインデックスの比較
  • 行数: スキャンされた行数(推定行数)
  • フィルタリング: テーブル条件によってフィルタリングされた行の割合
  • 追加:実行の説明と説明

上記の星印の付いたカテゴリは、遅いクエリを最適化するときによく使用されます。

2-2. 低速クエリ分析でよく使われる属性

1. タイプ:
テーブル アクセス メソッドは、MySQL がテーブル内の必要な行を検索する方法を示し、「アクセス タイプ」とも呼ばれます。

存在するタイプは、ALL、index、range、ref、eq_ref、const、system、NULL です (左から右、パフォーマンスは低から高の順)。ここでは、毎日目にする 3 つのタイプを紹介します。

  • ALL: (フルテーブルスキャン) MySQLはテーブル全体をスキャンして一致する行を検索します。フルテーブルスキャンと呼ばれることもあります。
  • index: (フルインデックススキャン) indexとALLの違いは、インデックスタイプはインデックスツリーのみを走査することです。
  • 範囲: インデックスを使用して行を選択し、指定された範囲内の行のみを取得します。

2. キー

key 列には、SQL で実際に使用されるインデックス (通常は possible_keys 列のインデックスの 1 つ) が表示されます。MySQL オプティマイザは通常、スキャン行の数を計算して、より適切なインデックスを選択します。インデックスが選択されていない場合は、NULL が返されます。もちろん、MySQL オプティマイザが間違ったインデックスを選択する場合もあります。SQL を変更することで、MySQL に「特定のインデックスを使用するか無視する」ように強制することができます。

  • インデックスの使用を強制する: FORCE INDEX (index_name)、USE INDEX (index_name)
  • インデックスを強制的に無視する: IGNORE INDEX (index_name)

3. 行
rows は、必要な行を見つけるために MySQL が読み取る (スキャンする) 必要があると推定する行数であり、正確ではない場合があります。

4. 追加
この列には重要な追加情報が表示されます。

インデックスの使用
クエリされた列はインデックスによってカバーされ、where フィルタ条件はインデックスの先頭の列であり、Extra は Using index です。つまり、テーブルに戻らずに、インデックス検索を通じて条件を満たすデータを直接見つけることができるということです。

注: 先頭列とは、通常、結合インデックスの最初の列または「最初の数列」を指しますが、単一列インデックスの場合も同様です。ここでは、理解しやすいように先頭列と呼びます。

where の使用
これは、MySQL サーバーがストレージ エンジンが行を取得した後に行をフィルター処理することを意味します。つまり、インデックスは使用されず、テーブルがクエリされます。

考えられる原因:

  • クエリされた列はインデックスによってカバーされていません。
  • フィルタ条件がインデックスの先頭列ではない場合、またはインデックスを正しく使用できない場合。

一時的な使用
これは、MySQL がクエリ結果をソートするときに一時テーブルを使用することを意味します。

ファイルソートの使用
これは、MySQL がテーブルからインデックス順に行を読み取るのではなく、外部インデックスを使用して結果を並べ替えることを意味します。

インデックス条件の使用
クエリ列がすべてインデックスに含まれているわけではなく、where 条件は先頭の列の範囲です。

where の使用; index の使用
クエリ対象の列はインデックスでカバーされており、where フィルタ条件はインデックス列の 1 つであるが、インデックスの先頭列ではない、またはインデックスの直接使用に影響を与えるその他の状況 (範囲フィルタ条件の存在など) があります。Extra は、Using where; Using index であり、条件を満たすデータはインデックス検索を通じて直接クエリすることができず、影響は大きくないことを意味します。

3. 低速クエリの最適化に関する経験を共有する

3-1. LIMITページングを最適化する

システム内でページングを必要とする操作は、通常、適切な order by 句を使用して、limit plus offset メソッドを使用して実装されます。対応するインデックスがある場合は通常効率は良好ですが、そうでない場合は MySQL は大量のファイル ソート操作を実行する必要があります。

非常に厄介な問題は、オフセットが非常に大きい場合、たとえば limit 1000000,10 のようなクエリの場合です。これは、MySQL が 1000000 レコードをクエリし、最後の 10 レコードのみを返す必要があることを意味します。前の 1000000 レコードは破棄されます。これは非常にコストがかかり、クエリが遅くなります。

このようなクエリを最適化する最も簡単な方法の 1 つは、すべての列をクエリするのではなく、可能な限りインデックス カバーリング スキャンを使用することです。次に、必要に応じて結合操作を実行し、必要な列を返します。これにより、オフセットが大きい場合の効率が大幅に向上します。

次のクエリの場合:

-- 実行時間: 1.379 秒
vio_basic_domain_info から * を選択し、LIMIT 1000000,10 を指定します。

分析結果を説明します。

mysql> EXPLAIN SELECT * from vio_basic_domain_info LIMIT 1000000,10;
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+---------+---------+-------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+---------+---------+-------+
| 1 | シンプル | vio_basic_domain_info | NULL | すべて | NULL | NULL | NULL | NULL | 1377809 | 100.00 | NULL |
+----+-------------+-----------------------+------------+-------+---------------+-------+---------+---------+---------+-------+
セットに 1 行、警告 1 件 (0.00 秒)

このステートメントの最大の問題は、制限 M,N のオフセット M が大きすぎるため、各クエリが最初にテーブル全体から条件を満たす最初の M 個のレコードを検索し、次にこれらの M 個のレコードを破棄して、M+1 番目のレコードから始めて、条件を満たす N 個のレコードを順番に検索することです。テーブルが非常に大きく、フィルター フィールドに適切なインデックスがなく、M が非常に大きい場合、このコストは非常に高くなります。

次に、次のクエリを前のクエリの後にマークされた位置から開始できる場合、条件を満たす 10 件のレコードを見つけて、次のクエリを開始する位置を書き留めておき、次のクエリをその位置から直接開始できるようにすれば、クエリを実行するたびにテーブル全体から条件を満たす最初の M 件のレコードを見つけて破棄し、M+1 から始めて条件を満たすさらに 10 件のレコードを見つける必要がなくなります。

一般的に、遅いページングクエリを処理するには次の方法があります。

アイデア1: カバーインデックスの構築

たとえば、SQL を変更してカバーリング インデックスを使用すると、app_name や createTime など、テーブル内のいくつかのフィールドのみをクエリする必要がある場合、app_name フィールドと createTime フィールドに結合インデックスを設定して、テーブル全体をスキャンせずにカバーリング インデックスを実現できます。この方法は、クエリ列が少ないシナリオに適していますが、クエリ列が多すぎるシナリオには推奨されません。
所要時間: 0.390秒

mysql> EXPLAIN SELECT app_name,createTime from vio_basic_domain_info LIMIT 1000000,10;
+----+-------------+-----------------------+------------+--------+---------------+--------------+----------+----------+----------+----------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+--------+---------------+--------------+----------+----------+----------+----------+
| 1 | SIMPLE | vio_basic_domain_info | NULL | index | NULL | idx_app_name | 515 | NULL | 1377809 | 100.00 | インデックスを使用 |
+----+-------------+-----------------------+------------+--------+---------------+--------------+----------+----------+----------+----------+
セットに 1 行、警告 1 件 (0.00 秒)

アイデア2: オフセットを最適化する

カバーリング インデックスを使用できない場合は、最初の 100 万個のデータをすばやくフィルター処理する方法を見つけることが重要です。自動インクリメント主キーの順序付き条件を使用して、最初に 1000001 番目のデータの ID 値をクエリし、次に 10 行後にクエリを実行できます。これは、主キー ID が自動インクリメントされるシナリオに適しています。
所要時間: 0.471秒

vio_basic_domain_infoから*を選択します。 
  id >=(vio_basic_domain_info から id を選択 ORDER BY id 制限 1000000,1) 制限 10;

原則: まず、インデックスに基づいて 1000001 番目のデータに対応する主キー ID の値を照会し、次に ID の値を介して ID の背後にある 10 個のデータに直接照会します。以下の EXPLAIN 分析結果では、この SQL の 2 段階の実行プロセスを確認できます。

mysql> EXPLAIN SELECT * from vio_basic_domain_info where id >=(SELECT id from vio_basic_domain_info ORDER BY id limit 1000000,1) limit 10;
+----+-------------+-----------------------+------------+--------+---------------+---------+---------+----------+----------+----------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+--------+---------------+---------+---------+----------+----------+----------+
| 1 | PRIMARY | vio_basic_domain_info | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 10 | 100.00 | where の使用 |
| 2 | サブクエリ | vio_basic_domain_info | NULL | インデックス | NULL | プライマリ | 8 | NULL | 1000001 | 100.00 | インデックスを使用 |
+----+-------------+-----------------------+------------+--------+---------------+---------+---------+----------+----------+----------+
セットに 2 行、警告 1 回 (0.40 秒)

方法3:「遅延関連付け」

所要時間: 0.439秒
遅延結合は、規模の大きいテーブルに適しています。SQL は次のとおりです。

SELECT * from vio_basic_domain_info inner join (select id from vio_basic_domain_info order by id limit 1000000,10) as myNew using(id);

ここでは、カバーリング インデックス + 遅延関連クエリを使用します。これは、最初に ID 列のみをクエリし、カバーリング インデックスを使用してページの 10 個のデータ ID をすばやく見つけ、返された 10 個の ID をテーブルに取得し、主キー インデックスを介して再度クエリすることと同じです。 (この方法は、テーブル データの急激な増加による影響が少なくなります。)

mysql> EXPLAIN SELECT * from vio_basic_domain_info inner join (select id from vio_basic_domain_info order by id limit 1000000,10) as myNew using(id);
+----+-------------+-----------------------+------------+----------+---------------+----------+-----------+-----------+------------+-------------+
| id | select_type | テーブル | パーティション | タイプ | 可能なキー | キー | キー長 | ref | 行 | フィルター済み | 追加 |
+----+-------------+-----------------------+------------+----------+---------------+----------+-----------+-----------+------------+-------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 1000010 | 100.00 | NULL |
| 1 | プライマリ | vio_basic_domain_info | NULL | eq_ref | プライマリ | プライマリ | 8 | myNew.id | 1 | 100.00 | NULL |
| 2 | DERIVED | vio_basic_domain_info | NULL | index | NULL | PRIMARY | 8 | NULL | 1000010 | 100.00 | インデックスを使用 |
+----+-------------+-----------------------+------------+----------+---------------+----------+-----------+-----------+------------+-------------+
セットに 3 行、警告 1 件 (0.00 秒)

3-2. インデックスが機能していないか確認する

ワイルドカード文字「%」を使用してファジークエリを開始しないようにしてください。そうしないと、データベース エンジンがインデックスを破棄し、テーブル全体のスキャンを実行します。次のように:

SELECT * FROM t WHERE username LIKE '%陈哈哈%'

最適化方法: フィールドの後にファジークエリを使用するようにします。次のように:

SELECT * FROM t WHERE username LIKE '陈哈哈%'

フロントでファジークエリを使用する必要があるとしたら、

  • MySQL 組み込み関数 INSTR(str,substr) を使用して一致させます。これは Java の indexOf() に似ており、文字列の添え字位置を見つけます。
  • 全文インデックスと一致検索を使用する
  • データ量が多い場合は、数十億のデータを数秒で取得できる ElasticSearch と Solr の使用をお勧めします。
  • テーブルのデータ量が少ない場合 (レコードが数千件) は、凝ったことはせず、単に '%xx%' のように使用してください。

しかし、MySQL の大きなフィールドのあいまい一致は欠陥であると言わざるを得ません。結局のところ、トランザクションの ACID 特性を確保するには、パフォーマンスが過度に消費されます。したがって、実際のシナリオで同様のビジネス ニーズがある場合は、ElasticSearch、Hbase などのビッグ データ ストレージ エンジンを思い切って変更することをお勧めします。感情の問題じゃないよ〜

エンジンがテーブル全体のスキャンを実行することになるため、not in の使用は避けてください。代わりに、次のように not exists を使用することをお勧めします。

-- インデックスを使用しないでください SELECT * FROM t WHERE name not IN ('Timo','Captain');

-- インデックスで移動します。select * from t as t1 where not exists (select * from t as t2 where name IN ('Timo','Captain') and t1.id = t2.id);

or を使用すると、データベース エンジンがインデックスを放棄し、テーブル全体のスキャンを実行することになるため、or の使用は避けてください。次のように:

SELECT * FROM t WHERE id = 1 OR id = 3

最適化方法: or の代わりに union を使用できます。次のように:

SELECT * FROM t WHERE id = 1
   連合
SELECT * FROM t WHERE id = 3

NULL 値の判断は避けてください。NULL 値を判断すると、データベース エンジンがインデックスを破棄し、テーブル全体のスキャンを実行することになります。次のように:

SELECT * FROM t WHERE スコアがNULL

最適化方法: フィールドにデフォルト値 0 を追加し、0 の値を判断できます。次のように:

SELECT * FROM t WHERE スコア = 0

where 条件の等号の左側で式や関数演算を実行しないようにしてください。そうしないと、データベース エンジンがインデックスを破棄し、テーブル全体のスキャンを実行することになります。
式と関数演算は等号の右側に移動できます。次のように:

-- テーブル全体のスキャン SELECT * FROM T WHERE score/10 = 9
-- インデックスへ移動 SELECT * FROM T WHERE score = 10*9

データ量が多い場合は、where 1=1 条件の使用を避けてください。通常、クエリ条件の組み立てを容易にするために、この条件をデフォルトで使用し、データベース エンジンはインデックスを放棄して完全なテーブル スキャンを実行します。次のように:

ユーザー名、年齢、性別を T から選択し、1=1 を指定します。

最適化方法: コードで SQL を組み立てるときに判断します。where 条件がない場合は、where 条件を削除します。where 条件がある場合は、and を追加します。

クエリ条件に<>または!=を使用しないでください
インデックス列をクエリの条件として使用する場合は、<> や != などの判定条件を使用しないでください。ビジネスで不等号記号を使用する必要がある場合は、このフィールドのインデックス作成を回避し、クエリ条件内の他のインデックス フィールドに置き換えるように、インデックス作成を再評価する必要があります。

where 条件には、複合インデックスの先頭以外の列のみが含まれます。たとえば、複合 (結合) インデックスには、key_part1、key_part2、key_part3 の 3 つの列が含まれますが、SQL ステートメントにはインデックスの先頭列「key_part1」が含まれていません。MySQL 結合インデックスの左端一致原則に従って、結合インデックスは使用されません。

-- インデックスなしで、key_part2=1 かつ key_part3=2 のテーブルから col1 を選択
-- インデックスで移動し、key_part1 =1、key_part2=1、key_part3=2 のテーブルから col1 を選択します

暗黙的な型変換により、インデックスは使用されません。次の SQL ステートメントは、インデックス列の型が varchar であるのに、指定された値が数値であり、暗黙的な型変換を伴うため、インデックスを正しく使用できません。

col_varchar=123 のテーブルから col1 を選択します。

要約する

さて、この記事を通じて、MySQL の遅いクエリを分析するためのいくつかの方法と経験を得られたことを願っています。遅いクエリは、MySQL では常に避けられない話題です。遅くなる原因はさまざまです。遅いクエリを完全に回避したい場合はどうすればよいでしょうか。若者よ、間違いから学ぶことを勧めます。

私たちがすべきことは、スロークエリをタイムリーに発見し、解決することです。実際、多くのスロークエリは、ある業務のデータ量が劇的に増加したときや、業務ニーズの変化によりフィールドが変更されたり、既存のインデックスが操作されたりしたときなど、受動的に発生します。それはあなたのせいではないが、それでもあなたは責任を負わなければならないかもしれない

これで、MySQL スロー クエリの落とし穴に関するこの記事は終了です。MySQL スロー クエリに関する関連コンテンツをさらにご覧になりたい場合は、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Docker MySQL コンテナでスロークエリログを有効にする方法
  • MySQL の遅いクエリの最適化ソリューション
  • MySQL で遅いクエリ SQL を見つけて最適化する詳細な例
  • MySQL最適化ソリューション: スロークエリログを有効にする
  • MySQLクエリが遅い理由
  • MySQLスローログに関する知識のまとめ
  • MySQL スロークエリログの詳細な理解
  • MySQL で遅い SQL を素早く見つける方法

<<:  jQuery プロジェクトで重複送信を防ぐ方法

>>:  フレームセットの高さを設定する際のインターフェース変形の解決策

推薦する

nginx をプロキシ キャッシュとして使用する方法

キャッシュを使用する目的は、バックエンドの負荷を軽減し、Web サイトの同時実行性を向上させることで...

Windows 上で Zookeeper サーバーを構築するチュートリアル

インストールと設定Apacheの公式ウェブサイトには多くのミラーダウンロードアドレスが用意されており...

MySQL 重複インデックスと冗長インデックスの例の分析

この記事では、例を使用して MySQL の重複インデックスと冗長インデックスについて説明します。ご参...

MySQLは変数を使用してさまざまなソートを実装します

コアコード -- 以下では、MySQLでのソート列の実装を示します -- テストデータ CREATE...

MySQLのカバーインデックスに関する知識ポイントのまとめ

インデックスにクエリする必要があるすべてのフィールドの値が含まれている(またはカバーしている)場合、...

Mysql 5.7.19 無料インストール バージョンで遭遇した落とし穴 (コレクション)

1. 公式ウェブサイトから 64 ビットの zip ファイルをダウンロードします。 2. インスト...

vue+el-element でファイル名に応じてダイアログを動的に作成する実践

目次背景成し遂げる1. カプセル化された /utils/dialogControl.js 2.ダイア...

MySQLでユーザー認証情報を表示する具体的な方法

具体的な方法: 1. コマンドプロンプトを開く2. mysql -u root -pコマンドを入力し...

VSCode 開発 UNI-APP 構成チュートリアルとプラグイン

目次前面に書かれた予防開発環境構築開発構成に関する注意事項前面に書かれたuni-app は、Vue....

ページング効果を実現するNode+Express

この記事では、ページング効果表示を実現するためのnode+expressの具体的なコードを参考までに...

MySQL 8.0 が起動できない 3534 の解決策

MySQL 8.0 サービスを開始できません最近、 cmdで MySQL サービスを起動するときに遭...

vue cli3は環境ごとにパッケージ化の手順を実装します

cli3 でビルドされた vue プロジェクトは、ゼロ構成ファイルとして知られています。パッケージ化...

Javascriptの基本ループの詳しい説明

目次サイクルのために入室のためのその間しながら行うループから抜け出す要約するサイクルのためにループは...

Vueはv-modelを使用してel-paginationコンポーネントのプロセス全体をカプセル化します。

v-model を使用してページング情報オブジェクトをバインドします。ページング情報オブジェクトに...