MySQLでSTRAIGHT_JOINを使用する場合


88

作業しているクエリがかなり複雑で、実行に8秒かかりました。EXPLAINは奇妙なテーブル順序を示していて、FORCEINDEXヒントを使用してもすべてのインデックスが使用されていませんでした。STRAIGHT_JOIN joinキーワードに出くわし、INNERJOINキーワードの一部をそれに置き換え始めました。かなりの速度向上に気づきました。最終的に、このクエリのすべてのINNER JOINキーワードをSTRAIGHT_JOINに置き換えたところ、0.01秒で実行されるようになりました。

私の質問は、いつSTRAIGHT_JOINを使用し、いつINNERJOINを使用するかです。適切なクエリを作成している場合、STRAIGHT_JOINを使用しない理由はありますか?

回答:


73

正当な理由がない限り、STRAIGHT_JOINの使用はお勧めしません。私自身の経験では、MySQLクエリオプティマイザは私が望むよりも頻繁に貧弱なクエリプランを選択しますが、一般的にそれをバイパスする必要があるほど頻繁ではありません。これは、常にSTRAIGHT_JOINを使用した場合に行うことです。

すべてのクエリを通常のJOINのままにしておくことをお勧めします。1つのクエリが次善のクエリプランを使用していることがわかった場合は、最初にクエリを少し書き直したり再構築したりして、オプティマイザがより適切なクエリプランを選択するかどうかを確認することをお勧めします。また、少なくともinnodbの場合は、インデックス統計が古くなっているだけではないことを確認してください(ANALYZE TABLE)。これにより、オプティマイザが不適切なクエリプランを選択する可能性があります。オプティマイザーのヒントは、通常、最後の手段です。

クエリヒントを使用しないもう1つの理由は、データ分布が時間の経過とともに変化したり、テーブルが大きくなるにつれてインデックスの選択性が変化したりする可能性があることです。現在最適なクエリヒントは、時間の経過とともに最適ではなくなる可能性があります。ただし、ヒントが古くなっているため、オプティマイザはクエリプランを適応させることができません。オプティマイザーに決定を許可すると、柔軟性が高まります。


59
この回答は、実際にいつ使用 するかを説明していませんstraight_join
Pacerier 2015年

23

MySQL JOINリファレンスから:

「STRAIGHT_JOINはJOINに似ていますが、左側のテーブルが常に右側のテーブルの前に読み取られる点が異なります。これは、結合オプティマイザがテーブルを間違った順序で配置する(少数の)場合に使用できます。」


27
ありがとう、でも私はすでにMySQLのマニュアルを読んでいます。いくつかのさらなる説明を期待しています。
グレッグ

20

これは、最近仕事で出てきたシナリオです。

A、B、Cの3つのテーブルについて考えてみます。

Aには3,000行あります。Bには300,000,000行あります。Cには2,000行あります。

外部キーが定義されています:B(a_id)、B(c_id)。

次のようなクエリがあるとします。

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

私の経験では、この場合、MySQLはC-> B-> Aを選択する可能性があります。CはAよりも小さく、Bは巨大で、すべて等結合です。

問題は、MySQLが(C.idとB.c_id)と(A.idとB.a_id)の共通部分のサイズを必ずしも考慮していないことです。BとCの間の結合がBと同じ数の行を返す場合、それは非常に不適切な選択です。Aから始めて、BをAと同じ数の行にフィルター処理する場合は、はるかに適切な選択でした。straight_joinこのようにこの順序を強制するために使用できます:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

a前に参加する必要がありますb

通常、結果のセットの行数が最小になる順序で結合を実行する必要があります。したがって、小さなテーブルから始めて、結果の結合も小さくなるように結合するのが理想的です。小さなテーブルから始めて、それを大きなテーブルに結合すると、大きなテーブルと同じ大きさになると、物事は洋ナシ型になります。

ただし、統計に依存します。データの分布が変わると、計算が変わる可能性があります。また、結合メカニズムの実装の詳細にも依存します。

MySQLで見た最悪のケースは、必須straight_joinまたは積極的なインデックスヒントを除いて、ライトフィルタリングを使用して厳密な並べ替え順序で大量のデータをページ分割するクエリです。MySQLは、ソートよりもフィルターと結合にインデックスを使用することを強く好みます。ほとんどの人はデータベース全体を並べ替えようとしているのではなく、クエリに応答する行のサブセットが限られているため、これは理にかなっています。限られたサブセットの並べ替えは、並べ替えられているかどうかに関係なく、テーブル全体をフィルタリングするよりもはるかに高速です。そうではありません。この場合、インデックス付きの列があるテーブルの直後に直接結合を配置して、固定されたもので並べ替えたいと思いました。


問題を解決するためにストレート結合をどのように使用しますか?
ハネレ

@Hannelestraight_joinは、右の前に左のテーブルを評価します。したがってA -> B -> C、私の例から移動したい場合は、最初のjoinキーワードをstraight_join。に置き換えることができます。
バリー・ケリー

きちんと。あなたの答えに例としてそれを含めることは有用でしょう:)
Hannele 2018年

18

MySQLは、複雑なクエリで結合順序を選択するのに必ずしも優れているわけではありません。複雑なクエリをstraight_joinとして指定することにより、クエリは指定された順序で結合を実行します。テーブルを最小公分母として最初に配置し、straight_joinを指定することで、クエリのパフォーマンスを向上させることができます。


11

STRAIGHT_JOIN、この句を使用して、JOIN順序を制御できます。つまり、外側のループでスキャンされるテーブルと、内側のループでスキャンされるテーブルです。


アウターループとインナーループとは何ですか?
Istiaque Ahmed 2017

@IstiaqueAhmedテーブルは、ネストされたループによって連結された(表AおよびループスローテーブルBからの最初の行を取る第2の行を取る...というようにここではテーブルAは、外側のループである。)されている
会計م

6

STRAIGHT_JOINを使用しなければならなかった理由を説明します。

  • 私が持っていたパフォーマンスのクエリの問題を。
  • クエリを単純化すると、クエリは突然効率的になりました
  • どの特定の部分が問題を引き起こしているのかを理解しようとして、私はただできませんでした。(2つの左結合は遅く、それぞれが独立して高速でした)
  • 次に、低速クエリと高速クエリの両方でEXPLAINを実行しました(左側の結合の1つを追加します)
  • 驚いたことに、MySQLは2つのクエリ間のJOIN順序を完全に変更しました。

したがって、結合の1つをstraight_joinにして、前の結合を最初に読み取るように強制しました。これにより、MySQLが実行順序を変更できなくなり、魅力のように機能しました。


2

私の短い経験でSTRAIGHT_JOINは、クエリが30秒から100ミリ秒に短縮された状況の1つは、実行プランの最初のテーブルが列による順序を持つテーブルではなかったことです。

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

オプティマイザーがstores 最初にヒットすることを選択した場合Using index; Using temporary; Using filesort

ORDERBYまたはGROUPBYに、結合キューの最初のテーブル以外のテーブルの列が含まれている場合、一時テーブルが作成されます。

ソース

ここでオプティマイザーは、sales最初にヒットするように指示することで、少し助けが必要です。

sales STRAIGHT_JOIN stores

1
(私はあなたの答えを飾りました。)
リックジェームス

2

あなたのクエリが終了するとした場合ORDER BY... LIMIT...可能やっにオプティマイザをだまして、クエリを再公式化するために最適でLIMIT 前にJOIN

(この回答はSTRAIGHT_JOIN、に関する元の質問だけに適用されるわけではなく、のすべてのケースに適用されるわけでもありません。STRAIGHT_JOIN。)

@Accountantمから始めると、これはほとんどの状況でより高速に実行されるはずです。(そしてそれはヒントを必要としない。)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

ノート:

  • まず、50個のIDがフェッチされます。これは、で特に高速になりINDEX(date, id)ます。
  • 次に、結合して、一時テーブルに移動せずにsales50個の「何でも」を取得できるようにします。
  • サブクエリは定義上順序付けられていORDER BYないため、外部クエリで繰り返す必要があります。(オプティマイザーは、実際に別のソートを実行しないようにする方法を見つける場合があります。)
  • はい、それは厄介です。しかし、通常は高速です。

「今日は速くても、明日は速くならないかもしれない」という理由で、ヒットを使用することに反対しています。


0

少し古いことはわかっていますが、これがシナリオです。特定のテーブルにデータを入力するためにバッチスクリプトを実行しています。ある時点で、クエリの実行が非常に遅くなりました。特定のレコードで結合順序が正しくなかったようです。

  • 正しい順序で

ここに画像の説明を入力してください

  • IDを1増やすと、順序が乱れます。「Extra」フィールドに注意してください

ここに画像の説明を入力してください

  • Straight_joinを使用すると、問題が修正されます

ここに画像の説明を入力してください

Straight_joinの実行をミリ秒単位で使用しているときに、誤った順序が約65秒間実行されます


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

3
これでは、直接結合が適切な場合を判断するのに十分な情報が得られません。
ハネレ2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.