0

4 つのテーブル間で LEFT JOIN を実行する比較的単純なクエリがあります。A は「メイン」テーブルまたは階層内の最上位のテーブルです。B は A にリンクし、C は B にリンクします。さらに、X は A にリンクします。したがって、階層は基本的に

A
C => B => A
X => A

クエリは基本的に次のとおりです。

SELECT
    a.*, b.*, c.*, x.*
FROM
    a
    LEFT JOIN b ON b.a_id = a.id
    LEFT JOIN c ON c.b_id = b.id
    LEFT JOIN x ON x.a_id = a.id
WHERE
    b.flag = true
ORDER BY
    x.date DESC
LIMIT 25

経由EXPLAINで、正しいインデックスが配置されていること、および組み込みの MySQL クエリ オプティマイザーがそれらのインデックスを正しく適切に使用していることを確認しました。

ここが奇妙な部分です...

クエリをそのまま実行すると、実行に約 1.1 秒かかります。

ただし、いくつかのチェックを行った後、ほとんどの SELECT フィールドを削除すると、速度が大幅に向上するようです。

代わりに、これを 2 段階のクエリ プロセスにすると、次のようになります。

  1. 最初のクエリは上記と同じですが、SELECT 句を only に変更しますSELECT a.idSELECT *
  2. 2 番目のクエリも上記と同じですが、WHERE 句を変更して、a.id IN以前のものではなく、クエリ 1 の結果のみを実行するようにします。

結果は劇的に異なります。最初のクエリでは 0.03 秒、2 番目のクエリでは 0.02 秒です。

この 2 段階のクエリをコードで実行すると、基本的にパフォーマンスが 20 倍向上します。

だからここに私の質問があります:

このタイプの最適化は、DB エンジン内で既に実行されているはずではありませんか? 実際に SELECT されるフィールドの違いが、クエリの全体的なパフォーマンスに違いをもたらすのはなぜですか?

結局のところ、まったく同じ 25 行を選択し、それらの 25 行のまったく同じ完全な内容を返すだけです。では、なぜパフォーマンスに大きな格差があるのでしょうか?

2012 年 8 月 24 日午後 13 時 02 分 (PDT) に追加

フィードバックをくれたeggyalとinvertedSpearに感謝します。まず、これはキャッシュの問題ではありません。両方のクエリを複数回 (約 10 回) 実行するテストを実行し、各アプローチを交互に実行しました。結果は、最初の (1 回のクエリ) アプローチで平均 1.1 秒、2 回目の (2 回のクエリ) アプローチで .03+.02 秒です。

インデックスに関しては、EXPLAIN を実行して、キーを通過していることを確認したと思っていましたが、ほとんどの場合はそうです。ただし、もう一度簡単なチェックを行ったところ、興味深いことが 1 つあります。

遅い「単一クエリ」アプローチでは、3 行目の「インデックスの使用」という追加のメモが表示されません。

+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys          | key               | key_len | ref                           | rows | Extra                                        |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | t1    | index  | PRIMARY                | shop_group_id_idx | 5       | NULL                          |  102 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t1.organization_id |    1 | Using where                                  |
|  1 | SIMPLE      | t0    | ref    | bundle_idx,shop_id_idx | shop_id_idx       | 4       | dbmodl_v18.t1.organization_id |  309 |                                              |
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t0.id              |    1 |                                              |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+

IDのみを照会する場合は、「インデックスを使用してます」と表示されますが:

+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys          | key               | key_len | ref                           | rows | Extra                                        |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | t1    | index  | PRIMARY                | shop_group_id_idx | 5       | NULL                          |  102 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t1.organization_id |    1 | Using where                                  |
|  1 | SIMPLE      | t0    | ref    | bundle_idx,shop_id_idx | shop_id_idx       | 4       | dbmodl_v18.t1.organization_id |  309 | Using index                                  |
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t0.id              |    1 |                                              |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+

奇妙なことは、どちらも使用されている正しいインデックスをリストしていることです...しかし、私はそれが疑問を投げかけていると思います:

なぜそれらが異なるのですか(他のすべての句がまったく同じであることを考慮して)?そして、これはなぜ遅いのかを示していますか?

残念ながら、MySQL のドキュメントには、結果の "Extra" 列が空白/null の場合の情報があまりありませんEXPLAIN

4

1 に答える 1

1

速度よりも重要なのは、クエリ ロジックに欠陥があることです。WHERE 句で LEFT JOIN された列をテストする場合 (NULL のテスト以外)、その結合を強制的に INNER JOIN のように動作させます。代わりに、次のことが必要です。

SELECT
    a.*, b.*, c.*, x.*
FROM
    a
    LEFT JOIN b ON b.a_id = a.id
        AND b.flag = true
    LEFT JOIN c ON c.b_id = b.id
    LEFT JOIN x ON x.a_id = a.id
ORDER BY
    x.date DESC
LIMIT 25

.*私の次の提案は、SELECT 内のすべての を調べることです。すべてのテーブルのすべての列が本当に必要ですか?

于 2012-08-24T20:17:12.623 に答える