2012-04-13 12 views
19

クエリのEXPLAINプランを見ると、最適化が最適になる場所をどのように判断できますか?EXPLAINプランに基づいてMySQLクエリを最適化する方法

最初にチェックするべき点は、良いインデックスが使用されているかどうかということですが、それを超えると私はちょっと困ります。過去に試行錯誤して、ジョインが実行される順序が改善の良い原点になることがあることが時々判明しましたが、実行計画を見てどのように判断できるのでしょうか?

質問を最適化する方法をよく理解したいと思っていますが(私は非常に感謝しています)、抽象的な話ではなく具体的​​なケースについて議論する方が簡単です。私は現在、このいずれかで壁に頭を叩いておりますので、あなたの考えははるかに高く評価されるだろう:私は実行計画の最終行を解釈する際に修正

 
id select_type table type  possible_keys key  key_len ref     rows Extra 
1 SIMPLE  S  const PRIMARY,l,p,f4 PRIMARY   2 const      1 Using temporary 
1 SIMPLE  Q  ref  PRIMARY,S  S    2 const     204 Using index 
1 SIMPLE  V  ref  PRIMARY,n,Q  Q    5 const,db.Q.QID   6 Using where; Using index; Distinct 
1 SIMPLE  R1  ref  PRIMARY,L  L    154 const,db.V.VID   447 Using index; Distinct 
1 SIMPLE  W  eq_ref PRIMARY,w  PRIMARY   5 const,db.R.RID,const  1 Using where; Distinct 
1 SIMPLE  R2  eq_ref PRIMARY,L  PRIMARY  156 const,db.W.RID,const  1 Using where; Distinct 

アムを次のように:

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

もしそうなら、私の問題は最後のステップで発生するフィルタリングにあります。条件がフィルタリングされない場合(例:WHERE `Col_1_to_3` IN (1,2,3))、クエリは非常に迅速に実行されます(約50ms)。ただし、条件によって選択された行(WHERE `Col_1_to_3` IN (1,2))が制限された場合、クエリにはかなりの時間(〜5秒)がかかります。制限が1つの一致(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 
+0

クエリも表示されますか? –

+0

@MarcusAdams:私は気にしませんが、何を探していますか?私はあなたが見ていることが分かっていればもっと学ぶことが多いと感じています... – eggyal

+0

col_1_to_3を参照していますが、EXPLAIN結果にそのような列が表示されません。あなたは、それが唯一の説明であるように質問をフレーズできれば、言い換えれば質問について話している段落を削除してから、質問は必要なく、答えははいです。一般的には、クエリ、スキーマ、および説明が必要です。それ以外の場合は、推測しています。 –

答えて

13

はあなたのために行くと何クエリがあるしているものに依存します。

通常、EXPLAIN内のUsing whereの各行には、インデックス(possible keysおよびkeys列)を使用する必要があります。これらはあなたのフィルターで、WHEREとONを含みます。それはUsing indexといっても良いです。これは、カバリングインデックスがあることを意味し、MySQLはテーブルデータの行を参照するのではなく、インデックスからデータを直接取得できます。

Using whereがない行は、多数の行を戻している行を参照する必要があります。これらは、表のすべての行の戻り値です。私はあなたの質問が何であるか分からないので、ここで気にするべきかどうか分からない。結果セットをフィルタリングしてサイズを縮小し、パフォーマンスを向上させてみてください。

一般的には、Using filesortまたはUsing temporaryが表示されないようにする必要がありますが、期待していない場合は唯一悪いです。

通常、FilesortはORDER句とともに表示されます。一般的に、MySQLはカバーイングインデックス(Using index)を使用して、行がサーバから順に返されるようにします。そうでない場合、MySQLはfilesortを使用して後でそれらを注文する必要があります。

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

+0

マーカスありがとう。私が最も奇妙なのは、最終テーブルのフィルタから生じるパフォーマンスの重要な違いです。したがって、問題は "行数が多い行を返す"行にはないように見えます。 – eggyal