3

次の2つのテーブルがMySQL(簡体字)にあります。インデックスを使用していないJOINを使用したMySQLクエリ

  • clicks(InnoDBの)
    • date_added列にインデックスが約70,000,000レコードを周りに含まれてい
    • linksテーブル
  • 内のレコードを参照する列 link_idを持っています
  • links(MyISAM)
    • 約65,000

の周りに、私はこれらのテーブルを使用して、いくつかの分析クエリを実行しようとしている、はるかに少ないレコードが含まれています。 2つの指定された日付の中で発生したクリックについて、いくつかのデータを取り出す必要があります。他のユーザーが選択したフィルタを他のテーブルを使用してリンクテーブルに追加します。

しかし、私の質問はインデックスの使用を中心に展開されています。次のクエリを実行すると、

私は1.40秒後に応答を返します。 EXPLAINを使用すると、MySQLはdate_addedカラムのインデックスを期待通りに使用しています。しかし

EXPLAIN SELECT COUNT(1) FROM clicks WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-11-16 23:59:59'; 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 
| 1 | SIMPLE  | clicks | range | date_added | date_added | 4  | NULL | 1559288 | Using where; Using index | 
+----+-------------+--------+-------+---------------+------------+---------+------+---------+--------------------------+ 

、私は私のlinksテーブル内LEFT JOIN私は、クエリを実行するのに非常に長い時間がかかることがわかり:6.50秒で完了し

SELECT 
    COUNT(1) AS clicks 
FROM 
    clicks AS c 
LEFT JOIN links AS l ON l.id = c.link_id 
WHERE 
    c.date_added >= '2016-11-01 00:00:00' 
AND c.date_added <= '2016-11-16 23:59:59'; 

。あなたはインデックスが大きなテーブルにdate_added列に使用し、はるかに長い時間がかかるように思われていない見ることができるように

EXPLAIN SELECT COUNT(1) AS clicks FROM clicks AS c LEFT JOIN links AS l ON l.id = c.link_id WHERE c.date_added >= '2016-11-01 00:00:00' AND c.date_added <= '2016-11-16 23:59:59'; 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra  | 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 
| 1 | SIMPLE  | c  | range | date_added | date_added | 4  | NULL   | 6613278 | Using where | 
| 1 | SIMPLE  | l  | eq_ref | PRIMARY  | PRIMARY | 4  | c.link_id  |  1 | Using index | 
+----+-------------+-------+--------+---------------+------------+---------+---------------+---------+-------------+ 

:私は、インデックスがdate_added列で使用されていなかったことがわかりEXPLAINを使用します。これは他のテーブルに参加するとさらに悪化するようです。

クリックの表のdate_added列のインデックスを使用するために何ができるのか誰にも分かりますか?


編集

私はちょうど別の方法を使用して、データベースの外に私の統計情報を取得しようとしました。私の方法の最初のステップは、クリックテーブルから別のセットのlink_idを引き出すことです。私は、JOINなしで、ここで再び同じ問題が発生していることがわかりました。インデックスが使用されていない:

マイクエリ:

SELECT 
    DISTINCT(link_id) AS link_id 
FROM 
    clicks 
WHERE 
    date_added >= '2016-11-01 00:00:00' 
AND date_added <= '2016-12-05 10:16:00' 

このクエリが完了するまでに、ほとんどの分を要しました。私はこの上EXPLAINを走ったと私はそれはと期待されるとして、クエリがインデックスを使用していないことが判明:

+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows  | Extra  | 
+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 
| 1 | SIMPLE  | clicks | index | date_added | link_id | 4  | NULL | 79786609 | Using where | 
+----+-------------+---------+-------+---------------+----------+---------+------+----------+-------------+ 
私はそれが結果セットを絞り込むために date_addedにインデックスを使用して、引き出すことが期待さ

distinct link_id値。なぜこれが起こっているのか?私はlink_idのインデックスとdate_addedを持っています。

+0

クエリの出力時間を短縮するのに役立つクリックテーブルのlink_idに**インデックス**を追加してください。 –

+0

@SumanEStatic - 'INDEX(link_id)'はヘルプではありません。 –

+0

MyISAMを使用しているような匂いがします。 'SHOW CREATE TABLE'を提供してください。 –

答えて

1

ない、絶対に確認ができますが、それは条件がそれがwherejoin on句が同等であるにもinner joinとは異なり、パフォーマンスの違いを作る(LEFT JOIN)外部結合を実行しているので、JOIN ON状態にWHERE状態から状態を移動することを検討。

SELECT COUNT(1) AS clicks 
FROM clicks AS c 
LEFT JOIN links AS l ON l.id = c.link_id 
AND (c.date_added >= '2016-11-01 00:00:00' 
AND c.date_added <= '2016-11-16 23:59:59'); 
+0

あなたの答えをありがとう。私はWHERE節からJOIN ONへ条件を移動しようとしましたが、私は同じ問題をまだ見ています。 – Jonathon

+0

テーブルがどのように関連しているかを言うのに 'ON'を使います。フィルタリングのために 'WHERE'を使います。オプティマイザはそれらを同じように扱います。それは 'EXPLAIN EXTENDED SELECT ... 'から見ることができます。警告を表示; ' –

+0

@ラフル私は、問題を診断するのに役立つ可能性のある何か他のものを使って私の質問を編集しました。ありがとう:) – Jonathon

1

あなたはLEFT JOINの代わりに通常のJOINを使用しますか? LEFT JOINは右側のすべての行を保持しているため、結合されていない表と同じ値のCOUNT()が生成されます。右側のテーブルから、左側のテーブルの行が一致する行のみを数えたい場合は、LEFT JOINではなくJOINを使用します。

date_addedにインデックスを追加し、(date_added, link_id)の複合インデックスに置き換えてみてください。 This sort of index is called a covering inde x。クエリプランナは、インデックスから必要なものをすべて得ることができると知っていれば、テーブルに戻ってくる必要はありません。この場合、クエリプランナはインデックスを日付範囲の先頭にランダムにアクセスして、範囲の末尾にindex range scanを追加します。しかし、もう一方のテーブルを参照する必要があります。

編集)実験の目的で、より狭い日付範囲を試してください。 EXPLAINが変更されているかどうかを確認してください。その場合、クエリプランナはdate_added列のカーディナリティを間違っていると推測している可能性があります。

index hintをお試しください。例えば、

SELECT COUNT(1) AS clicks 
    FROM clicks AS c USE INDEX (date_added) 
    LEFT JOIN links AS l ON l.id = c.link_id 
WHERE etc 

を試してみてくださいしかし、あなたのEXPLAIN出力から判断すると、あなたはすでにdate_addedの範囲スキャンをやっています。あなたの次のステップは、化合物をカバーするインデックスです。

links(id)にインデックスがあることを確認してください。恐らくそれはおそらくPKだからでしょう。

COUNT(1)の代わりにCOUNT(*)を試してみてください。おそらく違いはありませんが、試してみる価値があります。 COUNT(*)は、単にそれがカウントする各行の何かを評価するのではなく、行を数えるだけです。

(Nitpick)あなたの日付範囲は面白いです。最良の結果を得るには、範囲の最後に<を使用してください。

WHERE c.date_added >= '2016-11-01' 
    AND c.date_added < '2016-11-17'; 

編集:見て、MySQLのクエリプランナはテーブルが構成されている方法についての内部知識をたくさん使用しています。また、は、テーブルあたり1つのインデックスのみを使用して、2016年後半のクエリを満たすことができます。これは制限です。

SELECT DISTINCT columnは、問題のcolumnを重複排除する必要があるため、実際はかなり複雑なクエリです。その列にインデックスがある場合、クエリプランナはそれを使用する可能性があります。そのインデックスを選択することは、他のインデックスを選択できないことを意味します。

時々の化合物索引がありますが、必ずしもそうではありません。はこの種の索引選択ジレンマを解決し、索引の二重使用を許可します。このすべてについてはhttp://use-the-index-luke.com/

で読むことができますが、操作上の制約によって複合インデックスの追加が妨げられる場合は、1秒間のクエリを実行する必要があります。それはそれほど悪くはありません。あなたが仕事を得るために複合インデックスを追加することはできませんと言ってもちろん

は、このようなものです:

:ものは高速道路上で私のトラックから落ちています。

B:タップを物にかけ、それを結ぶ。

:私の上司は、トラックに防水シートを置くことはできません。

:うーん、その後、遅くドライブする。

+0

あなたの答えをありがとう。 'LEFT JOIN'の代わりに' JOIN'を使用してみましたが、成功しなかったので、 'COUNT(*)'も使ってみました。私は 'links(id)'が主キーで、 'clicks(link_id)'カラムもインデックスされていることを確認できます。私は現時点では、テーブルのサイズが大きいためテーブルを変更することには消極的です。したがって、インデックスを削除して、提案されたようにカバーインデックスを追加することはできませんでした。もう一度ありがとう! – Jonathon

+0

既存のインデックスを先に削除しなくても、新しいインデックスを追加できます。 –

+0

'ALTER TABLE'を使うと、任意の数のインデックスを同時に追加したり削除することができます。 –

関連する問題