2011-07-25 12 views
0

チケットの購入を示すデータベース〜800k個のレコードがあります。すべてのテーブルはInnoDBです。スロークエリは、次のとおりです。MySQLの低速クエリの最適化

SELECT e.id AS id, e.name AS name, e.url AS url, p.action AS action, gk.key AS `key` 
FROM event AS e 
    LEFT JOIN participation AS p ON p.event=e.id 
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person 
WHERE p.person='139160' 
    OR p.person IS NULL; 

このクエリは、それゆえp.personの引用PDOから来ています。 JOINおよびWHEREで使用されるすべての列が索引付けされます。 p.eventは、e.idに制限された外部キーであり、gk.issuedtoおよびp.personは、上記の表に拘束されている外部キーであるperson.idです。これらはすべてINTです。テーブルeは小さく、わずか10行です。表pは〜500,000行で、gkは空です。

このクエリは、人の詳細ページで実行されます。すべてのイベントのリストを取得したい場合は、参加行がある場合は参加し、ゴールデンキー行がある場合はゴールデンキーがある場合。

スロークエリログを提供します:

Query_time: 12.391201 Lock_time: 0.000093 Rows_sent: 2 Rows_examined: 466104 

EXPLAIN SELECTが与える:

+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra  | 
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ 
| 1 | SIMPLE  | e  | ALL | NULL   | NULL  | NULL | NULL   | 10 |    | 
| 1 | SIMPLE  | p  | ref | event   | event | 4  | msadb.e.id  | 727 | Using where | 
| 1 | SIMPLE  | gk | ref | issuedto  | issuedto | 4  | msadb.p.person | 1 |    | 
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+ 

をこのクエリでは、将来的に与えられたp.personその後、< 0.05sのための最初の実行に7〜12秒で動作します。 OR p.person IS NULLを削除しても、クエリ時間は改善されません。 pのサイズが〜20kから〜500k(古いデータのインポート)に増加したとき、このクエリは右に減速しました。

パフォーマンスを向上させる方法についてご意見はありますか?全体的な目的を覚えているのは、すべてのイベントのリストを取得することです。参加行がある場合は参加し、ゴールデンキー行がある場合はゴールデンキーがある場合。複数のクエリがより効率的になるなら、私はそれを行うことができます。にやにや笑いのために

+1

p.eventとe.id、gk.issuedtoとp.personで結合を行っているのを見て、それらの列にインデックスを作成すると役に立ちます。 ...遅いタグ...あなたは私の一日を作った。 – bdares

+0

ありがとう@bdares、あなたがタグを楽しんでうれしい;)。私はそれをリストアップしていませんでしたが、実際には 'e.id'、' gk.issuedto'と 'p.person'はすべてインデックスされています。 – mjec

答えて

1

あなたは次のことを試してみて、それが助けかどうp.person IS NULLと離れて行うことができる場合:

SELECT e.id AS id, e.name AS name, e.url AS url, p.action AS action, gk.key AS `key` 
FROM event AS e 
    LEFT JOIN participation AS p ON (p.event=e.id AND p.person='139160') 
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person 
+0

これでは結果が異なる可能性がありますか? –

+0

@Mitch私は、join条件にwhere条件をとり、IS NULLを削除しました。この方法では、すべてのレコードが一致した後ではなく、結合時にフィルタリングが行われます。少なくともそれは考えています:) –

+0

:ジョイン条件に条件を渡すと異なる結果セットになる可能性があります –

0

...あなたの選択にキーワード「STRAIGHT_JOIN」を追加...

SELECT STRAIGHT_JOIN ... rest of query... 
0

私はあなたが持っているどのように多くのインデックスはわからないし、あなたのテーブルのスキーマが、null値を使用しないようしてみてくださいデフォルトでは、クエリを劇的に遅くする可能性があります。

0

インデックスが2列、イベントや人で構成されているので、私は参加にインデックスを追加または変更しようとするだろう。

(編集)

このための私の推論は、クエリオプティマイザが本当の賢いでない場合、それは参加にイベントインデックスを使用しますが、あなたは、いくつかのイベントを持っているので、パーティションの数が多いをスキャンすることですそのイベントを持って、where句からperson値を持つレコードを探します。参加者のイベント・インデックスに人をタグ付けすることで、照会オプティマイザーは、そのイベントのすべての参加をスキャンしないようにすることができます。

+0

ありがとう。実感できるようだが、実際には 'イベント、人は'すでに '参加'のユニークなインデックスです。 – mjec

0

特定の人物のルックアップを行っている場合は、その人物IDフィルタがあるので推測しています。私は、クエリを逆にしようとするので、最初に人のテーブルを検索してから、次にすべてのイベントを与える追加のクエリを作成します。

SELECT 
    e.id AS id, e.name AS name, e.url AS url, 
    p.action AS action, gk.key AS `key` 
FROM person AS p 
    JOIN event AS e ON p.event=e.id 
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person 

UNION 

SELECT 
    e.id AS id, e.name AS name, e.url AS url, 
    NULL, NULL 
FROM event AS e 

明らかにあなたが最初のクエリは一致しますが、簡単に、または多分変数を使用して、そのにe.idを選択することで、全体のことを中心に選択を包むことによって解決thatsの場合には重複イベントを持っている意味します。この最初のクエリでは、2番目のクエリで変数を使用して(これが動作するかどうかは確かではありませんが、それをテストしていないが、なぜか分からない)。