2012-03-12 15 views
1

私はDjango管理者の検索がなぜ遅いのか調べてみました(here参照)。さらに掘り下げてみると、MySQL(5.1、InnoDBテーブル)のパフォーマンスはあるクエリから別のものへと大きく変わることがわかりました。例えば:I場合なぜMySQL(InnoDB)のパフォーマンスには多くのバリエーションがありますか?

SELECT DISTINCT `donnees_artiste`.`id` 
    FROM `donnees_artiste` 
LEFT OUTER JOIN `donnees_artiste_evenements` 
    ON (`donnees_artiste`.`id` = `donnees_artiste_evenements`.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` 
    ON (`donnees_artiste_evenements`.`evenement_id` = `donnees_evenement`.`id`) 
LEFT OUTER JOIN `donnees_artiste_evenements` T4 
    ON (`donnees_artiste`.`id` = T4.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` T5 
    ON (T4.`evenement_id` = T5.`id`) 
LEFT OUTER JOIN `donnees_artiste_evenements` T6 
    ON (`donnees_artiste`.`id` = T6.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` T7 
    ON (T6.`evenement_id` = T7.`id`) 

WHERE (
    (`donnees_artiste`.`nom` LIKE '%c%' 
    OR `donnees_artiste`.`prenom` LIKE '%c%' 
    OR `donnees_evenement`.`cote` LIKE '%c%' 
    OR `donnees_evenement`.`titre` LIKE '%c%') 
AND (`donnees_artiste`.`nom` LIKE '%d%' 
    OR `donnees_artiste`.`prenom` LIKE '%d%' 
    OR T5.`cote` LIKE '%d%' 
    OR T5.`titre` LIKE '%d%') 
AND (`donnees_artiste`.`nom` LIKE '%e%' 
    OR `donnees_artiste`.`prenom` LIKE '%e%' 
    OR T7.`cote` LIKE '%e%' 
    OR T7.`titre` LIKE '%e%') 
); 

このクエリで89ミリ秒を取得し、3093行を返しジャンゴによって生成された(4分野での 'C'、 'D' と 'E'、関連の2を探して) 'e'を 'k'で置き換えると、ほとんど同じクエリですが、8720ミリ秒(100倍の増加)を返して931行を返します。

これらのクエリの両方が同じEXPLAINを与えるので、そこに手掛かりはありません。私は最初のクエリにCOUNTをすれば

ID SELECT_TYPE  TABLE TYPE POSSIBLE_KEYS KEY  KEY_LEN  REF  ROWS EXTRA 
1 SIMPLE donnees_artiste  ALL  None None None None 4368 Using temporary; Using filesort 
1 SIMPLE donnees_artiste_evenements ref  artiste_id,donnees_artiste_evenements_eb99df11 artiste_id 4 mmac.donnees_artiste.id  1 Using index; Distinct 
1 SIMPLE donnees_evenement eq_ref PRIMARY,donnees_evenements_id_index  PRIMARY  4 mmac.donnees_artiste_evenements.evenement_id 1 Using where; Distinct 
1 SIMPLE T4 ref  artiste_id,donnees_artiste_evenements_eb99df11 artiste_id 4 mmac.donnees_artiste.id  1 Using index; Distinct 
1 SIMPLE T5 eq_ref PRIMARY,donnees_evenements_id_index  PRIMARY  4 mmac.T4.evenement_id 1 Using where; Distinct 
1 SIMPLE T6 ref  artiste_id,donnees_artiste_evenements_eb99df11 artiste_id 4 mmac.donnees_artiste.id  1 Using index; Distinct 
1 SIMPLE T7 eq_ref PRIMARY,donnees_evenements_id_index  PRIMARY  4 mmac.T6.evenement_id 1 Using where; Distinct 

また、それは11200ミリ秒を取ります。

SELECT COUNT(DISTINCT `donnees_artiste`.`id`) 
    FROM `donnees_artiste` 
LEFT OUTER JOIN `donnees_artiste_evenements` 
    ON (`donnees_artiste`.`id` = `donnees_artiste_evenements`.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` 
    ON (`donnees_artiste_evenements`.`evenement_id` = `donnees_evenement`.`id`) 
LEFT OUTER JOIN `donnees_artiste_evenements` T4 
    ON (`donnees_artiste`.`id` = T4.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` T5 
    ON (T4.`evenement_id` = T5.`id`) 
LEFT OUTER JOIN `donnees_artiste_evenements` T6 
    ON (`donnees_artiste`.`id` = T6.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` T7 
    ON (T6.`evenement_id` = T7.`id`) 

WHERE (
    (`donnees_artiste`.`nom` LIKE '%c%' 
    OR `donnees_artiste`.`prenom` LIKE '%c%' 
    OR `donnees_evenement`.`cote` LIKE '%c%' 
    OR `donnees_evenement`.`titre` LIKE '%c%') 
AND (`donnees_artiste`.`nom` LIKE '%d%' 
    OR `donnees_artiste`.`prenom` LIKE '%d%' 
    OR T5.`cote` LIKE '%d%' 
    OR T5.`titre` LIKE '%d%') 
AND (`donnees_artiste`.`nom` LIKE '%e%' 
    OR `donnees_artiste`.`prenom` LIKE '%e%' 
    OR T7.`cote` LIKE '%e%' 
    OR T7.`titre` LIKE '%e%') 
); 

My innodb_buffer_pool_sizeが高く設定されています。私はすべての関連フィールドとプライマリキーのインデックスを持っており、私はすでにテーブルを最適化しました。

なぜ、最初のクエリが非常に高速で、2つのクエリが非常に遅いのですか?これらの3つのクエリは例に過ぎません。多くの時間私はちょうどクエリから1文字を変更または削除しており、クエリ時間に大きな違いをもたらしました。しかし、私はどんなパターンも見ることができません。

UPDATE

間違いなく、Djangoはこれらのクエリを生成する方法から来るパフォーマンスの問題。これらのすべての冗長性は、一緒に接続されてパフォーマンスを低下させます。現時点では、Django SQLジェネレータのバグか、検索フィールド用のクエリの作成方法や、Django開発者が期待したとおりに動作するかどうかのバグかどうかは、私には分かりません。私はまだ調査中ですが、Djangoの動作に少なくとも1つの奇妙なことがあります...

このクエリを実行すると(必ずしも2番目のものと同じではないが遠くない)結果がかなり速くなります(161ミリ秒、キャッシュなし):

SELECT DISTINCT `donnees_artiste`.`id` 
    FROM `donnees_artiste` 
LEFT OUTER JOIN `donnees_artiste_evenements` 
    ON (`donnees_artiste`.`id` = `donnees_artiste_evenements`.`artiste_id`) 
LEFT OUTER JOIN `donnees_evenement` 
    ON (`donnees_artiste_evenements`.`evenement_id` = `donnees_evenement`.`id`) 

WHERE (
    (`donnees_artiste`.`nom` LIKE '%c%' 
    OR `donnees_artiste`.`prenom` LIKE '%c%' 
    OR `donnees_evenement`.`cote` LIKE '%c%' 
    OR `donnees_evenement`.`titre` LIKE '%c%') 
AND (`donnees_artiste`.`nom` LIKE '%d%' 
    OR `donnees_artiste`.`prenom` LIKE '%d%' 
    OR `donnees_evenement`.`cote` LIKE '%d%' 
    OR `donnees_evenement`.`titre` LIKE '%d%') 
AND (`donnees_artiste`.`nom` LIKE '%k%' 
    OR `donnees_artiste`.`prenom` LIKE '%k%' 
    OR `donnees_evenement`.`cote` LIKE '%k%' 
    OR `donnees_evenement`.`titre` LIKE '%k%') 
); 

SECOND UPDATE

が最後にそれはジャンゴのバグではありません、私はそれが必要な動作だと確信しています。アイデアは、マルチターム検索では、次のタームの検索は前のタームでサブセットのリターンで行われるため、関連フィールドではすべてのタームが同じ行にある必要はありません一致。このために、DBは各サブセットを持つ一時テーブルを作成してスキャンする必要があります。これは、最初の用語がほんの数行に一致し、一時テーブルが小さくなり、次の用語の検索が高速になる(なぜなら、それらは小さなテーブル上で実行されるため)、バリエーションが多い理由を説明します。 2つのクエリの違いは微妙ですが、Djangoクエリは一般的により多くの一致を返すことができます。タイプの

+1

速い遅い - それは速く言葉の前で起こる文字である必要があり、私はそれが唯一の限りをスキャン想像必要に応じて言葉にする。それらの中文字 'LIKE'コマンドはほとんど効率的ではありません。 – Orbling

+0

あなたの言葉の先頭に現れる手紙の権利。 'newtover'への私のコメントを見てください。それらの中間文字のLIKEコマンドについては、テキスト内の任意の文字列を検索したいと思っていません。 – Etienne

+0

InnoDBはそのようなテキストフィールドを読むためのセットアップではありません。私が必死に行う必要がある場合、私は通常、MyISAMテーブルにデータをシャドーし、 'MATCH()'で[FULLTEXT'インデックスを使う](http://dev.mysql.com/doc/refman/5.1/ en/fulltext-search.html)。それは通常4文字の単語で停止するので、それは単一の文字には良いことではありませんが。それを変更するためのサーバー全体の設定は、非効率的です。 – Orbling

答えて

2

ほとんどの場合、eは、スキャンされた文字列の始めと最初の検索文字列にあり、OR条件を短くすることができると考えています。kの一致は最後の条件とどこかで発生します文字列の最後に。そして、kで行が大幅に少なくなるので、より多くの文字列をマッチすることなくフルスキャンする必要があります。

+0

あなたはそうです。私の4つのフィールドのうちの1つはほぼ「E」で始まります。つまり、基本的には、MySQLがどれくらいの時間検索する必要があるかということです。したがって、クエリで結果が返されない場合は、すべてをスキャンする必要があるため、このクエリは確実に長くなります。 – Etienne

1

条件:

WHERE column LIKE '%c%' 

columnにインデックスを使用することはできません。したがって、これらの列は完全にスキャンする必要があります。

このような条件が複数あり、それらの間にORを使用しています(これらすべてのテーブルがスキャンされることが保証されています)。最後に、結果を返す前におそらく最終的なファイルを必要とするDISTINCTを追加します(むしろDjangoです)。

パフォーマンスの大きな違い(100x)の説明が見つかりません。最初のクエリがキャッシュされた可能性があります。クエリの最後にORDER NY NULLを追加して時間を計ってみることはできますか?

生成されたクエリも、おそらくミニデカルト結合で終了するため、あまりうまく設計されていません。複数の表に基本表を結合すると、基本表と1対多の関係になります。これがパフォーマンスの低下の原因であり、クエリプランはそれを明確にするのに役立ちます。

+0

大きな違いの説明については、 'newtover'の答えを参照してください。生成されたクエリはうまく設計されていませんが、「ミニデカルト結合」とは何を意味するのですか?そして「クエリプラン」によって? – Etienne

+0

クエリプランは、 'EXPLAIN SELECT yourQuery'が示すものです。 –

+0

デカルト結合とは、アーティストに(平均して)10個の 'donnees_evenement'行がある場合、(WHERE条件なしの)クエリは' 10x10x10xNumberOfArtists = 1000xNumberOfArtists'行を返します。問合せプランナは、実際に非常に多くの行を含む一時表を生成し、WHERE条件(複雑なOR-AND条件とDINSTINCTによってより良い計画を生成できない場合がある)をチェックするプランを実際に生成します。 –

2

先頭のワイルドカードでLIKEパターンを使用すると、クエリはインデックスに役立ちません。このようにLIKEを使用すると、非常に非効率的になり、その実行時間はかなり変動する可能性があります。 WHY?

  1. LIKEステートメントの背後にあるアルゴリズムは、一致した場合に行の検索を停止します。
  2. (インデックスを使用しない)このシナリオでは、MySQLはいくつかの場合に適用可能な場合と適用されない場合がある他の追加アルゴリズムを適用します。

なぜ3番目のクエリでCOUNTを使用すると、それほど遅くなるのですか?

あなたはinnoDBを使用しています。

innoDBは、MyISAMのように(カラムがNOT NULLの場合)格納/キャッシュされた値から行数を読み込みません。なぜなら、innoDBは 'reading'(MyISAMに対抗する)よりも「書く」ために最適化されているからです。 innoDBテーブルでCOUNTを使用すると、毎回フルテーブルスキャンかフルインデックススキャンを実行します。

クエリでインデックスを使用していないと、最悪の場合がありますので、フルテーブルスキャンが発生します(はい、それは聞こえるほど遅いです)。

は、私はあなたに興味があるかもしれないと思った:MySQL Indexes「K」に、「E」の

+0

MyISAMではこれらのクエリの方が高速であることを真剣に疑っています。 –

+0

私はMyISAMの移行を推奨しておらず、innoDBはEtienneのニーズに合っていると仮定していますが、私が知っているのはMyISAMがカウント値をキャッシュするということです。 :) –

+0

ええ、それはあなたが言うようにするかもしれません。ただし、テーブル全体のCOUNT(*)が必要な場合にのみ。複雑なJoinとWhere condiitonsを持つ 'COUNT DISTINCT someColumn'ではありません。 –

関連する問題