21

クエリのEXPLAINプランを見て、どこで最適化を行うのが最適かをどのように判断しますか?

最初に確認すべきことの 1 つは、適切なインデックスが使用されているかどうかということですが、それ以上は少し困惑しています。過去の試行錯誤の結果、結合が実行される順序が改善の良いソースになり得ることが時々わかりましたが、実行計画を見てそれをどのように判断できますか?

クエリを最適化する方法についての一般的な理解を深めたいと思っていますが (読んでいただければ幸いです!)、抽象的に話すよりも具体的なケースについて話し合う方が簡単であることもわかっています。私は現在これで頭を壁にぶつけているので、あなたの考えは大歓迎です:

id select_type テーブル タイプ possible_keys キー key_len ref 行 エクストラ
 1 SIMPLE S const PRIMARY,l,p,f4 PRIMARY 2 const 1 一時的な使用
 1 SIMPLE Q ref PRIMARY,SS 2 const 204 インデックスの使用
 1 SIMPLE V ref PRIMARY,n,QQ 5 const,db.Q.QID 6 where の使用; インデックスの使用; 明確
 1 SIMPLE R1 ref PRIMARY,LL 154 const,db.V.VID 447 インデックスの使用。明確
 1 SIMPLE W eq_ref PRIMARY,w PRIMARY 5 const,db.R.RID,const 1 where を使用。明確
 1 SIMPLE R2 eq_ref PRIMARY,L PRIMARY 156 const,db.W.RID,const 1 where の使用; 明確

実行計画の最後の行を次のように解釈するのは正しいですか。

  • 主キーが完全に一致するため、R2出力行ごとにフェッチする必要があるのは 1 行のみです。
  • R2ただし、そのような出力行は、 ?に適用されるいくつかの基準に基づいてフィルタリングされます。

もしそうなら、私の問題はその最終ステップで発生するフィルタリングにあります。条件の結果がフィルタリングされない場合 (例: WHERE `Col_1_to_3` IN (1,2,3))、クエリは非常に高速に実行されます (~50ms)。ただし、条件によって選択された行が制限されている場合 ( WHERE `Col_1_to_3` IN (1,2))、クエリの所要時間はかなり長くなります (最大 5 秒)。制限が単一の一致 ( WHERE `Col_1_to_3` IN (1)) にある場合、オプティマイザはまったく異なる実行計画を提案します (5 秒よりわずかに優れていますが、それでも 50 ミリ秒よりはるかに悪い)。そのテーブルで使用できるより良いインデックスがあるようには見えません (結果ごとに 1 つの行を返すために既に主キーを完全に使用していることを考えると?)。

このすべての情報をどのように解釈すべきでしょうか? このような出力フィルタリングは結合される最終テーブルで行われるため、以前にテーブルに結合してそのような行をより早くフィルタリングするのに比べて、かなりの労力が無駄になっていると推測できますか? R2もしそうなら、実行計画のいつ参加すべきかをどのように判断しますか?

ここにクエリとスキーマを完全に含めることには抵抗しましたが (単に答えを言われるだけでなく、何を探すべきかを知っている可能性が非常に高いため)、議論を進める必要があることは理解しています。

SELECT DISTINCT
    `Q`.`QID`
FROM
    `S`
    NATURAL JOIN `Q`
    NATURAL JOIN `V`
    NATURAL JOIN `R` AS `R1`
    NATURAL JOIN `W`

    JOIN `R` AS `R2` ON (
            `R2`.`SID` = `S`.`SID`
        AND `R2`.`RID` = `R1`.`RID`
        AND `R2`.`VID` = `S`.`V_id`
        AND `R2`.`Col_1_to_3` IN (1,2) -- this is where performance suffers!
    )

WHERE
    AND `S`.`SID` = @x
    AND `W`.`WID` = @y
;

テーブルの定義は次のとおりRです。

CREATE TABLE `R` (
  `SID` smallint(6) unsigned NOT NULL,
  `RID` smallint(6) unsigned NOT NULL,
  `VID` varchar(50) NOT NULL DEFAULT '',
  `Col_1_to_3` smallint(1) DEFAULT NULL,
  `T` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`SID`,`RID`,`VID`),
  KEY `L` (`SID`,`VID`,`Col_1_to_3`),
  CONSTRAINT `R_f1` FOREIGN KEY (`SID`) REFERENCES `S` (`SID`),
  CONSTRAINT `R_f2` FOREIGN KEY (`SID`, `VID`) REFERENCES `V` (`SID`, `VID`),
  CONSTRAINT `R_f3` FOREIGN KEY (`SID`, `VID`, `Col_1_to_3`) REFERENCES `L` (`SID`, `VID`, `LID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
4

1 に答える 1

16

目的とクエリの内容によって異なります。

一般に、EXPLAIN の行ごとに、インデックス (および列)Using whereを使用する必要があります。これらはフィルターで、WHERE と ON が含まれます。言われるとさらに良いです。これは、カバーするインデックスがあることを意味し、MySQL はテーブル データの行にアクセスするのではなく、インデックスから直接データを取得できます。possible keyskeysUsing index

がなくUsing where、多数の行が返されている行を確認する必要があります。これらは、テーブル内のすべての行の値を返しています。私はあなたの質問が何であるかわからないので、ここで心配する必要があるかどうかわかりません. 結果セットをフィルタリングして、サイズを縮小し、パフォーマンスを向上させてみてください。

通常、Using filesortまたはを見Using temporaryないようにする必要があります。

Filesort は通常、ORDER 句とともに表示されます。Using index通常、サーバーから順番に行が返されるように、MySQL でカバリング インデックス ( ) を使用する必要があります。そうでない場合、MySQL は後でファイルソートを使用してそれらを並べ替える必要があります。

Using temporary派生テーブルにはインデックスがないため、派生テーブルを参照するとうまくいかない可能性があります。インデックス付きの一時テーブルを明示的に作成したようですので、ここでは悪くありません。場合によっては、派生テーブルを使用するしか選択肢がないことがありUsing temporaryます。

于 2012-04-13T22:57:33.160 に答える