2016-09-28 12 views
2

なぜ私のクエリが極端に遅いのか分かりません。 48ギガバイトDDR3デュアルXeonプロセッサL5630の60秒PHP7.0-FPMとMariaDB 10.0.27でのUbuntu 16.04を実行している特定のテーブルでLEFT JOINが非常に遅い

SELECT v.video_id, v.user_id, v.title, v.slug, v.rating, v.rated_by, 
          v.duration, v.thumb, v.total_views, v.total_comments, v.add_time, 
          v.view_time, v.status, v.source_id, v.orientation, v.thumbs, 
          v.featured, v.flagged, 
          u.username, 
          s.name, 
          f.reason, 
          GROUP_CONCAT(c.name) AS categories 
        FROM video AS v 
        LEFT JOIN video_flags AS f ON (f.video_id = v.video_id) 
        LEFT JOIN video_sources AS s ON (s.source_id = v.source_id) 
        LEFT JOIN user AS u ON (u.user_id = v.user_id) 
        LEFT JOIN video_category AS vc ON (vc.video_id = v.video_id) 
        LEFT JOIN video_categories AS c ON (c.category_id = vc.category_id) GROUP BY v.video_id ORDER BY v.video_id DESC LIMIT 10 

私がコメントするときので、私は、video_flags表にあるように、問題を突き止めましたf.reasonフィールドとvideo_flagsの左側の結合では、クエリは152msしかかかりません。私は戻って、次の取得]を選択説明実行するとINT(11)

をvideo_flagsテーブルがVIDEO_IDにインデックスを持ち、フィールドタイプは、両方のテーブルに同じである:

+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref      | rows | Extra           | 
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+ 
| 1 | SIMPLE  | v  | ALL | NULL   | NULL  | NULL | NULL      | 1219933 | Using temporary; Using filesort     | 
| 1 | SIMPLE  | f  | ALL | video_id  | NULL  | NULL | NULL      |  1 | Using where; Using join buffer (flat, BNL join) | 
| 1 | SIMPLE  | s  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.v.source_id |  1 |             | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.v.user_id  |  1 |             | 
| 1 | SIMPLE  | vc | ref | video_id  | video_id | 4  | adb_network.v.video_id  |  2 | Using index          | 
| 1 | SIMPLE  | c  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.vc.category_id |  1 | Using where          | 
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+---------+-------------------------------------------------+ 

私は何を知りません私はここで紛失しています。まず、video_flagsテーブルが空であると思っていました。次にレコードを追加して、クエリを素早く(200ms)実行しましたが、問題は戻っています。

ご迷惑をおかけして申し訳ありません。


更新:

+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref      | rows | Extra  | 
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+ 
| 1 | SIMPLE  | v  | index | NULL   | PRIMARY | 4  | NULL      | 5 |    | 
| 1 | SIMPLE  | f  | ref | video_id  | video_id | 4  | adb_network.v.video_id  | 1 | Using index | 
| 1 | SIMPLE  | s  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.v.source_id | 1 |    | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.v.user_id  | 1 |    | 
| 1 | SIMPLE  | vc | ref | video_id  | video_id | 4  | adb_network.v.video_id  | 2 | Using index | 
| 1 | SIMPLE  | c  | eq_ref | PRIMARY  | PRIMARY | 4  | adb_network.vc.category_id | 1 | Using where | 
+------+-------------+-------+--------+---------------+----------+---------+----------------------------+------+-------------+ 

ソリューション:@somniumによって示唆されるように、私はFORCE INDEX上を追加しようとしたは@somniumためf.reason列のない選択を説明追加video_idの列とそれによってクエリ時間が60秒から272msに短縮されました。なぜなら、結合中にインデックスが失われても問題が解決される理由はまだ分かりません。ありがとう

SELECT v.video_id, v.user_id, v.title, v.slug, v.rating, v.rated_by, 
           v.duration, v.thumb, v.total_views, v.total_comments, v.add_time, 
           v.view_time, v.status, v.source_id, v.orientation, v.thumbs, 
           v.featured, v.flagged, 
           u.username, 
           s.name, 
           f.reason, 
           GROUP_CONCAT(c.name) AS categories 
         FROM video v 
         LEFT JOIN video_flags f FORCE INDEX FOR JOIN (video_id) ON (f.video_id = v.video_id) 
         LEFT JOIN video_sources s ON (s.source_id = v.source_id) 
         LEFT JOIN user u ON (u.user_id = v.user_id) 
         LEFT JOIN video_category vc ON (vc.video_id = v.video_id) 
         LEFT JOIN video_categories c ON (c.category_id = vc.category_id) GROUP BY v.video_id ORDER BY v.video_id DESC LIMIT 10 
+0

ビデオテーブルに1219933があり、他のいくつかのテーブルに参加しています。このテーブルにはフィルタがありません。したがって、これらのすべての行が複数の結合で使用されます。 60秒は本当に良いと思う。しかし、あなたがまだ持っていない場合は、video_idにインデックスを追加してみることができます – e4c5

+0

ご意見ありがとうございます。不思議なのは私のクエリが複数の結合を行うことです。問題はf.reason列が含まれている場合にのみ発生します。私がそれをコメントアウトすると、他のすべての結合がそのまま残っているだけです。 video_flags.video_idにインデックスがあるので、どちらでも問題ありません。 – Hugo

+2

あなたはグループ化していないSELECTのすべての列を集計すべきですか? MySQLはそうすることができますが、それは悪い習慣です。 –

答えて

2

大きなテーブルでフルテーブルスキャンを誤って実行していますvideos。潜在的な問題のリストはat the MySQL documentationです。

潜在的な問題f.reasonせずにあなたの説明を見てみると

行方不明のキー

、オプティマイザはvideo_flagsテーブルを無視します。これにより、MySQL/MariaDBはすべてのインデックスを完全に利用することができます。

f.reasonを追加する場合、MySQLはv.video_id = f.video_idと一致する必要があります。 video_flagsには1つの行があるため、MySQLはvideoの各エントリに対してv.video_idを取得しようとします。 v.video_idにインデックスがないようです。したがって、video_idを取得するには、MySQLはディスク/メモリから完全なvideosテーブルをスキャンする必要があります。これにより、のないexplain selectの5と比較して1219933行が検索されます。

低カーディナリティ

別の潜在的な問題は、低カーディナリティですが、私は正確にオプティマイザがこれを台無しになり本当にわかりません。

MySQLのドキュメントから:

あなたは 別のカラムに通して(多くの行がキー値と一致)カーディナリティの低いキーを使用しています。この場合、MySQLはキーを使用することによっておそらく が多くのキー検索を行い、テーブルスキャンが高速になると想定しています。

私の理解では、原因video_flagsでは非常に低いカーディナリティ(1-2値)に、それはあなたが、左からのすべての値を常に必要になります(MySQLが参加による左へvideosに完全なテーブルをルックアップすることがありますということです側)。この時点で、フル・テーブル・スキャンが優れていると判断します。これは、カーディナリティが高いためにvideo_idを使用しているその他の場合には発生しません。 FORCE INDEX構文を使用して、インデックスの使用を強制することができます。

潜在的なソリューション

検索を高速化するためにv.video_idにインデックスを追加してみてください。 explain selectsを慎重にチェックして、突然使用されていないインデックスを見つけてください。 NULLpossible_keysテーブルvのスロー選択で注意してください。

FORCE INDEXを試してみてください。

希望に役立ちます。

+0

あなたの答えをありがとう、 'video_id'は' video'テーブルの主キーですので、 'v.video_id'にはインデックスがあります。最初の 'explain select'を見ると、' video'テーブルと 'video_flags'テーブルの両方のキー列にNULLと表示されます。主キーが何とか使われていないようですね? – Hugo

+1

https://dev.mysql.com/doc/refman/5.5/en/how-to-avoid-table-scan.htmlから探しています。たぶん 'FORCE INDEX'があなたを助けるかもしれません。 – somnium

+0

@somniumに 'FORCE INDEX'を追加していただきありがとうございます。問題を修正したようです。元の投稿を最終的なクエリで更新します。それがなぜインデックスを失うのか分かりませんが、あなたの提案は私の一日を助けました。 – Hugo

0

プランA:これが効果的かどうかを確認してください。 (すべてのことが、参加またはあなたが望む10 video_idsを取得するために、グループ化を経由する必要がないと思われます。)

SELECT ... -- as before 
    FROM (
     SELECT video_id 
      FROM video 
      ORDER BY video_id DESC 
      LIMIT 10) AS v1 
    JOIN video AS v USING (video_id) 
    LEFT JOIN ... -- as before 
    ... 
    ORDER BY video_id DESC; -- no GROUP BY or LIMIT here 

プランB:LEFTは

s.name, 
LEFT JOIN video_sources AS s ON (s.source_id = v.source_id) 

サブクエリにJOINを回し - >

(SELECT name FROM video_sources WHERE source_id = v.source_id) AS name, 

他の単一行の値とその左結合の場合はDittoです。