2011-08-11 33 views
2
を注文する際にあまりにも遅い

アプリのそれの自己についての詳細(編集済み)も参照してください。 Simple but heavy application consuming a lot of resources. How to Optimize? (採用されたソリューションを使用した両方の結合とフルテキスト検索)最適化MySQLのクエリ:

次のクエリは、約500.000行まで25秒で実行しています。 ORDERを削除すると、0.5秒かかります。

・ファーストテスト

ORDERを維持し、すべてのトンを取り除きます。とtu。クエリには7秒かかります。

セカンドテスト

私は、応答時間を追加したり、i.created_atフィールドにインデックスを削除した場合と同じまま。

QUERY:

は** EDITED:。私はSLOW DOWN QUERY BY GROUP BYとORDER(BOTH私も参加する変更クエリで少し利得を達成しましたことに気付きました利得は10秒になりましたが、問題は残ります)。修正とここ

SELECT SQL_NO_CACHE 
     DISTINCT `i`.`id`, 
     `i`.`entity`, 
     `i`.`created_at`, 
     `i`.`collected_at`, 

     `t`.`status_id` AS `twt_status_id`, 
     `t`.`user_id` AS `twt_user_id`, 
     `t`.`content` AS `twt_content`, 
     `tu`.`id` AS `twtu_id`, 
     `tu`.`screen_name` AS `twtu_screen_name`, 
     `tu`.`profile_image` AS `twtu_profile_image` 


     FROM `mtrt_items` AS `i` 

     LEFT JOIN `mtrt_users` AS `u` ON i.user_id =u.id 

     LEFT JOIN `twt_tweets_content` AS `t` ON t.id =i.id 
     LEFT JOIN `twt_users` AS `tu` ON u.id = tu.id 

     INNER JOIN `mtrt_items_searches` AS `r` ON i.id =r.item_id 
     INNER JOIN `mtrt_searches` AS `s` ON s.id =r.search_id 
     INNER JOIN `mtrt_searches_groups` AS `sg` ON sg.search_id =s.id 
     INNER JOIN `mtrt_search_groups` AS `g` ON sg.group_id =g.id 
     INNER JOIN `account_clients` AS `c` ON g.client_id =c.id     

    ORDER BY `i`.`created_at` DESC 
    LIMIT 100 OFFSET 0 

を、EXPLAINはfilesortレコードを返すために停止しているが、静止画は "一時的な使用" を返す** EXPLAIN(編集)です:

+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+ 
| id | select_type | table | type | possible_keys  | key  | key_len | ref     | rows | Extra      | 
+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+ 
| 1 | SIMPLE  | c  | index | PRIMARY   | PRIMARY | 4  | NULL     | 1 | Using index; Using temporary | 
| 1 | SIMPLE  | g  | ref | PRIMARY,client_id | client_id | 4  | clubr_new.c.id   | 3 | Using index     | 
| 1 | SIMPLE  | sg | ref | group_id,search_id | group_id | 4  | clubr_new.g.id   | 1 | Using index     | 
| 1 | SIMPLE  | s  | eq_ref | PRIMARY   | PRIMARY | 4  | clubr_new.sg.search_id | 1 | Using index     | 
| 1 | SIMPLE  | r  | ref | search_id,item_id | search_id | 4  | clubr_new.s.id   | 4359 | Using where     | 
| 1 | SIMPLE  | i  | eq_ref | PRIMARY   | PRIMARY | 8  | clubr_new.r.item_id | 1 |        | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY   | PRIMARY | 8  | clubr_new.i.user_id | 1 | Using index     | 
| 1 | SIMPLE  | t  | eq_ref | PRIMARY   | PRIMARY | 4  | clubr_new.i.id   | 1 |        | 
| 1 | SIMPLE  | tu | eq_ref | PRIMARY   | PRIMARY | 8  | clubr_new.u.id   | 1 |        | 
+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+ 

ここにありますmtrt_items表:

+--------------+-------------------------------------------------------+------+-----+---------+----------------+ 
| Field  | Type             | Null | Key | Default | Extra   | 
+--------------+-------------------------------------------------------+------+-----+---------+----------------+ 
| id   | bigint(20)           | NO | PRI | NULL | auto_increment | 
| entity  | enum('twitter','facebook','youtube','flickr','orkut') | NO | MUL | NULL |    | 
| user_id  | bigint(20)           | NO | MUL | NULL |    | 
| created_at | datetime            | NO | MUL | NULL |    | 
| collected_at | datetime            | NO |  | NULL |    | 
+--------------+-------------------------------------------------------+------+-----+---------+----------------+ 

CREATE TABLE `mtrt_items` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `entity` enum('twitter','facebook','youtube','flickr','orkut') COLLATE utf8_unicode_ci NOT NULL, 
    `user_id` bigint(20) NOT NULL, 
    `created_at` datetime NOT NULL, 
    `collected_at` datetime NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `mtrt_user_id` (`user_id`), 
    KEY `entity` (`entity`), 
    KEY `created_at` (`created_at`), 
    CONSTRAINT `mtrt_items_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `mtrt_users` (`id`) ON DELETE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=309650 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

twt_tweets_contentは、MyISAMテーブルで、fulltext検索にも使用されています

+-----------+--------------+------+-----+---------+-------+ 
| Field  | Type   | Null | Key | Default | Extra | 
+-----------+--------------+------+-----+---------+-------+ 
| id  | int(11)  | NO | PRI | NULL |  | 
| user_id | int(11)  | NO | MUL | NULL |  | 
| status_id | varchar(100) | NO | MUL | NULL |  | 
| content | varchar(200) | NO | MUL | NULL |  | 
+-----------+--------------+------+-----+---------+-------+ 
+0

各ベンチマークの前に、RESET QUERY CACHEを実行していることを確認してください。右? – dfb

+0

'ORDER BY' i'.'id' DESC'を試してみるとどうなりますか?一般的にはまったく同じ結果が得られます。 idは自動インクリメントであるため、古いidはレコードが過去にさらに作成されたことを意味します。 –

+0

@spinning_plate実際には、SELECT SQL_NO_CACHE ...を使用します。 –

答えて

5

代わりのメインクエリにOrder Byを配置し、そのように、それをラップ:

SELECT * FROM ( 
    ... your query 
) ORDER BY `created at` 

は、クエリプランを見てみましょう。あなたのケースでは、外部結合が実行される前にテーブルmtrt_itemsにソートが実行されています。私が部分的に提供しているリライトでは、外部ジョインの後にソートが適用され、より小さなセットに適用されます。あなたが参加するのいずれかを実行する前にトップを実行することができますように

UPDATE

LIMITが大集合(50万?)に適用されていると仮定すると、それは見えます。

SELECT * from (
    SELECT 
    `id`, ... `created_at`, ... 
    ORDER BY `i`.`created_at` DESC 
    LIMIT 100 OFFSET 0) as i 

    LEFT JOIN `mtrt_users` AS `u` ON i.user_id =u.id 

    LEFT JOIN `twt_tweets_content` AS `t` ON t.id =i.id 
    LEFT JOIN `twt_users` AS `tu` ON t.user_id = tu.id 

    INNER JOIN `mtrt_items_searches` AS `r` ON i.id =r.item_id 
    INNER JOIN `mtrt_searches` AS `s` ON s.id =r.search_id 
    INNER JOIN `mtrt_searches_groups` AS `sg` ON sg.search_id =s.id 
    INNER JOIN `mtrt_search_groups` AS `g` ON sg.group_id =g.id 
    INNER JOIN `account_clients` AS `c` ON g.client_id =c.id     

GROUP BY i.id 
+0

私が他のコメントで言ったように...同じ時間。 –

+0

また、説明が「索引の使用;一時的な使用; filesortの使用」を戻していることを確認してください。多分問題? –

+0

すべてのfilesortはソートしていることを意味します。それは悪い名前です。それは効果的にクイックソートです。これを苛立たせるのは、それがあなたの言うとおりであれば、ソートは実際には100個のアイテムしか返さない巨大なセットをソートすることです。それが100個のアイテムを引っ張るテーブルが、たとえば500,000個のアイテムである場合、これがすべてあなたの速度を低下させる理由です。 –

0

あなたの最初のクエリでVARCHAR/TEXTフィールドを含めないでください。これにより、MEMORYエンジンを使用してソートに必要なTEMPORARYテーブルが作成され、効率が大幅に向上します。後で別のクエリを使用してテキストフィールドを収集し、ソートせずに、単にPRIMARY KEYフィールドの条件を使用して、(使用していると仮定して)スクリプト内のデータをマージすることができます。
JOINs(INNERまたはOUTER)も実際にデータを取得しないでください。