MySQL の高度な - インデックスの最適化とクエリの最適化 (2)

5. ソートの最適化

5.1 ソートの最適化

質問: WHERE 条件フィールドにインデックスを追加しますが、ORDER BY フィールドにインデックスを追加する必要があるのはなぜですか?


最適化の提案:

  • SQL では、WHERE 句と ORDER BY 句でインデックスを使用して、WHERE 句での完全なテーブル スキャンと ORDER BY 句での FileSort の並べ替えを回避できます。もちろん、場合によっては、テーブル全体のスキャンや FileSort の並べ替えが必ずしもインデックス作成より遅いとは限りません。ただし、一般に、クエリの効率を向上させるためには、依然としてこれを回避する必要があります。

  • Index を使用して ORDER BY 並べ替えを完了してみてください。WHERE と ORDER BY の後に同じ列が続く場合は、単一のインデックス列を使用し、それらが異なる場合は、結合インデックスを使用します。

  • Index が使用できない場合は、FileSort メソッドのチューニングが必要です。

INDEX a_b_c(a,b,c)
order by 能使用索引最左前缀
- ORDER BY a
- ORDER BY a,b
- ORDER BY a,b,c
- ORDER BY a DESC,b DESC,c DESC
如果WHERE使用索引的最左前缀定义为常量,则order by 能使用索引
- WHERE a = const ORDER BY b,c
- WHERE a = const AND b = const ORDER BY c
- WHERE a = const ORDER BY b,c
- WHERE a = const AND b > const ORDER BY b,c
不能使用索引进行排序
- ORDER BY a ASC,b DESC,c DESC /* 排序不一致 */
- WHERE g = const ORDER BY b,c /*丢失a索引*/
- WHERE a = const ORDER BY c /*丢失b索引*/
- WHERE a = const ORDER BY a,d /*d不是索引的一部分*/
- WHERE a in (...) ORDER BY b,c /*对于排序来说,多个相等条件也是范围查询*/

5.2 ケースの実践

ORDER BY 句では、インデックス ソートを使用し、FileSort ソートの使用を避けてください。
ケースを実行する前に、学生のインデックスをクリアして、主キーのみを残します。

DROP INDEX idx_age ON student;
DROP INDEX idx_age_classid_stuno ON student;
DROP INDEX idx_age_classid_name ON student;
#或者
call proc_drop_index('atguigudb2','student');

シナリオ: 30 歳で学生番号が 101000 未満の学生をユーザー名で並べ替えてクエリします。

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY
    -> NAME ;
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra                       |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 498917 |     3.33 | Using where; Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
1 row in set, 2 warnings (0.00 sec)

クエリ結果は次のとおりです。

mysql> SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY
    -> NAME ;
+-----+--------+--------+------+---------+
| id  | stuno  | name   | age  | classId |
+-----+--------+--------+------+---------+
| 695 | 100695 | bXLNEI |   30 |     979 |
| 322 | 100322 | CeOJNY |   30 |      40 |
| 993 | 100993 | DVVPnT |   30 |     340 |
| 983 | 100983 | fmUNei |   30 |     433 |
| 946 | 100946 | iSPxRQ |   30 |     511 |
| 469 | 100469 | LTktoo |   30 |      69 |
|  45 | 100045 | mBZrKC |   30 |     280 |
| 635 | 100635 | nQnUJL |   30 |     732 |
|  16 | 100016 | NzjxKh |   30 |     539 |
| 363 | 100363 | OMuKtM |   30 |     695 |
| 293 | 100293 | qOYywO |   30 |     586 |
| 169 | 100169 | qUElsg |   30 |     526 |
| 798 | 100798 | rhHPdX |   30 |      71 |
| 749 | 100749 | TCgaJe |   30 |     697 |
| 157 | 100157 | TUQtvY |   30 |      22 |
| 580 | 100580 | UHDUOj |   30 |     423 |
| 532 | 100532 | XvmZkc |   30 |     861 |
| 939 | 100939 | yBlCbB |   30 |     320 |
| 710 | 100710 | yhmRvD |   30 |     219 |
| 266 | 100266 | YueogP |   30 |     524 |
+-----+--------+--------+------+---------+
20 rows in set, 1 warning (0.16 sec)

結論: type は ALL で、これは最悪のケースです。ファイルソートの使用はエクストラにも表示されますが、これも最悪のシナリオです。最適化は必須です。

最適化のアイデア:

オプション 1: ファイルソートを削除するには、インデックスを構築します。

#创建新索引
CREATE INDEX idx_age_name ON student(age,NAME);

オプション 2: フィルター条件と並べ替えに上位インデックスを使用してみてください。

3 つのフィールドを組み合わせたインデックスを作成します。

DROP INDEX idx_age_name ON student;
CREATE INDEX idx_age_stuno_name ON student (age,stuno,NAME);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME;
mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND stuno <101000 ORDER BY NAME;
+----+-------------+---------+------------+-------+--------------------+--------------------+---------+------+------+----------+---------------------------------------+
| id | select_type | table   | partitions | type  | possible_keys      | key                | key_len | ref  | rows | filtered | Extra                                 |
+----+-------------+---------+------------+-------+--------------------+--------------------+---------+------+------+----------+---------------------------------------+
|  1 | SIMPLE      | student | NULL       | range | idx_age_stuno_name | idx_age_stuno_name | 9       | NULL |   20 |   100.00 | Using index condition; Using filesort |
+----+-------------+---------+------------+-------+--------------------+--------------------+---------+------+------+----------+---------------------------------------+
1 row in set, 2 warnings (0.00 sec)
mysql> SELECT SQL_NO_CACHE * FROM student
    ->  WHERE age = 30 AND stuno <101000 ORDER BY NAME ;
+-----+--------+--------+------+---------+
| id  | stuno  | name   | age  | classId |
+-----+--------+--------+------+---------+
| 695 | 100695 | bXLNEI |   30 |     979 |
| 322 | 100322 | CeOJNY |   30 |      40 |
| 993 | 100993 | DVVPnT |   30 |     340 |
| 983 | 100983 | fmUNei |   30 |     433 |
| 946 | 100946 | iSPxRQ |   30 |     511 |
| 469 | 100469 | LTktoo |   30 |      69 |
|  45 | 100045 | mBZrKC |   30 |     280 |
| 635 | 100635 | nQnUJL |   30 |     732 |
|  16 | 100016 | NzjxKh |   30 |     539 |
| 363 | 100363 | OMuKtM |   30 |     695 |
| 293 | 100293 | qOYywO |   30 |     586 |
| 169 | 100169 | qUElsg |   30 |     526 |
| 798 | 100798 | rhHPdX |   30 |      71 |
| 749 | 100749 | TCgaJe |   30 |     697 |
| 157 | 100157 | TUQtvY |   30 |      22 |
| 580 | 100580 | UHDUOj |   30 |     423 |
| 532 | 100532 | XvmZkc |   30 |     861 |
| 939 | 100939 | yBlCbB |   30 |     320 |
| 710 | 100710 | yhmRvD |   30 |     219 |
| 266 | 100266 | YueogP |   30 |     524 |
+-----+--------+--------+------+---------+
20 rows in set, 1 warning (0.00 sec)

その結果、filesort の SQL の実行速度は、filesort の最適化された SQL の実行速度を上回り、はるかに高速になり、結果はほぼ瞬時に表示されました。

結論は:

  1. 2 つのインデックスが同時に存在し、MySQL が自動的に最適なソリューションを選択します。(この例では、mysql は idx_age_stuno_name を選択します)。ただし、データ量が変化すると、選択されるインデックスも変化します。
  2. [範囲条件] フィールドと [グループ化または並べ替え] フィールドのいずれかを選択する場合、フィルタリングされたデータが十分にあり、並べ替える必要があるデータがそれほど多くない場合は、条件フィールドのフィルタリング量を観察することが優先されます。の場合、フィールドの範囲内にインデックスを配置することが優先されます。逆に。

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

双方向ソート (遅い)

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

  • ディスクから並べ替えフィールドを取得し、バッファー内で並べ替えてから、ディスクから他のフィールドを取得します。

データのバッチを取得するには、ディスクを 2 回スキャンする必要があります。ご存知のとおり、IO には非常に時間がかかるため、mysql4.1 以降、2 番目の改良されたアルゴリズムである単方向ソートが登場しました。

一方向ソート (高速)

クエリに必要なすべての列をディスクから読み取り、列ごとの順序に従ってバッファ内で並べ替えてから、並べ替えられたリストをスキャンして出力します。これはより効率的であり、データの 2 回目の読み取りを回避します。また、ランダム IO をシーケンシャル IO に変換しますが、各行をメモリに保存するため、より多くのスペースを使用します。

結論と提起された疑問

  • シングル パスは後ろから出てくるため、デュアル パスよりも一般的に優れています。
  • ただし、単一チャネルの使用には問題があります

6. GROUP BY の最適化

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

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

最適化アイデア 1

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

mysql> EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10)
    -> a
    -> WHERE t.id = a.id;
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table      | partitions | type   | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL | 498917 |   100.00 | NULL        |
|  1 | PRIMARY     | t          | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | a.id |      1 |   100.00 | NULL        |
|  2 | DERIVED     | student    | NULL       | index  | NULL          | PRIMARY | 4       | NULL | 498917 |   100.00 | Using index |
+----+-------------+------------+------------+--------+---------------+---------+---------+------+--------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)

最適化アイデア 2

このソリューションは、自動インクリメント主キーを持つテーブルに適しており、特定の場所で Limit クエリをクエリに変換できます。

mysql> EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    1 |   100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

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

8.1 カバリングインデックスとは何ですか?

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

方法 2 を理解する: 非クラスター化複合インデックスの形式。クエリ内の SELECT、JOIN、および WHERE 句で使用されるすべての列が含まれます (つまり、インデックスの構築に使用されるフィールドは、クエリ条件に関係するフィールドとまったく同じです)
。 。簡単に言うと、インデックス列 + 主キーには、SELECT から FROM までクエリされた列が含まれます。

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

利点:

  1. Innodb テーブル インデックスの二次クエリを避ける (テーブル リターン)

  2. ランダム IO をシーケンシャル IO に変換してクエリ効率を向上させることができます

欠点:
インデックス フィールドのメンテナンスには常にコストがかかります。したがって、カバーインデックスをサポートするために冗長インデックスを構築する際には、考慮すべきトレードオフがあります。これはビジネス DBA、つまりビジネス データ アーキテクトの仕事です。

おすすめ

転載: blog.csdn.net/qq_51495235/article/details/133102814