9
explain
select
    *
from
    zipcode_distances z 
inner join
    venues v    
    on z.zipcode_to=v.zipcode
inner join
    events e
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

「郵便番号 92108 から 5 マイル以内の会場でのイベント」をすべて検索しようとしていますが、このクエリを最適化するのに苦労しています。

説明は次のようになります。

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra

1, SIMPLE, e, ALL, idx_venue_id, , , , 60024, 
1, SIMPLE, v, eq_ref, PRIMARY,idx_zipcode, PRIMARY, 4, comedyworld.e.venue_id, 1, 
1, SIMPLE, z, ref, idx_zip_from_distance,idx_zip_to_distance,idx_zip_from_to, idx_zip_from_to, 30, const,comedyworld.v.zipcode, 1, Using where; Using index

「e」テーブルで完全なテーブルスキャンを取得していますが、高速化するために作成する必要があるインデックスがわかりません。

アドバイスをいただければ幸いです

ありがとうございました

4

4 に答える 4

11

質問の出力に基づいて、クエリで使用する必要EXPLAINのあるすべてのインデックスがすでにあります。

CREATE INDEX idx_zip_from_distance
  ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);

idx_zip_from_distance(インデックス名から、実際に列が含まれているかどうかはzipcode_toわかりません。含まれていない場合は、カバーインデックスにするために追加する必要があります。また、完全を期すためにvenues.id列を含めましたが、これがidx_zipcodeテーブルとInnoDBを使用している場合は、とにかく自動的に含まれます。)

ただし、MySQLは別の、場合によっては最適ではないクエリプランを選択しているようです。このプランでは、すべてのイベントをスキャンし、会場と郵便番号を見つけてから、距離で結果をフィルタリングします。イベントテーブルのカーディナリティが十分に低い場合、これ最適なクエリプランである可能性がありますが、この質問をしているという事実から、そうではないと思います。

最適ではないクエリプランの理由の1つは、プランナーを混乱させるインデックスが多すぎるという事実である可能性があります。たとえば、格納するデータがおそらく対称であるとすると、郵便番号テーブルにこれら3つのインデックスすべてが本当に必要ですか?個人的には、上記のインデックスに加えて、一意のインデックス(人工的なインデックスがない場合は主キーにもなります)を(できればこの順序で)追加することをお勧めします。それを利用してください)。(zipcode_to, zipcode_from)zipcode_to=?

ただし、私が行ったいくつかのテストに基づくと、MySQLが間違ったクエリプランを選択している主な問題は、単にテーブルの相対的なカーディナリティにあると思われます。おそらく、実際のzipcode_distancesテーブルは巨大であり、MySQLは、句の条件が実際にどれだけWHEREそれを絞り込むかを理解するのに十分なほど賢くはありません。

もしそうなら、最良かつ最も簡単な修正は、MySQLに必要なインデックスを使用させることです

select
    *
from
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance)
inner join
    venues v    
    FORCE INDEX (idx_zipcode)
    on z.zipcode_to=v.zipcode
inner join
    events e
    FORCE INDEX (idx_venue_id)
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

そのクエリを使用すると、実際に目的のクエリプランを取得する必要があります。(クエリプランナーだけで、提案されたインデックスの代わりにテーブルスキャンを使用することを決定でき、目的を無効にすることができるFORCE INDEXため、ここで必要です。これは、最初にテストしたときに発生しました。)USE INDEX

追伸 これは、SQLizeのデモで、問題がある場合とない場合の両方、問題を示しています。 FORCE INDEX

于 2013-01-01T01:28:33.913 に答える
1

両方のテーブルの列にインデックスを付けましたか?

e.id and v.venue_id

そうしないと、両方のテーブルにインデックスが作成されます。既に持っている場合は、1 つ以上のテーブルにほとんどレコードがなく、アナライザーがインデックス付き読み取りよりもフル スキャンを実行する方が効率的であることを検出した可能性があります。

于 2012-12-31T23:47:05.200 に答える
0

サブクエリを使用できます:

select * from zipcode_distances z, venues v, events e
where
    z.id in (select id from zipcode z where z.zipcode_from='92108' and z.distance <= 5)
    and z.zipcode_to=v.zipcode
    and v.id=e.venue_id
于 2012-12-31T23:32:58.830 に答える
-1

すべてのテーブルからすべての列を選択している(select *)ため、クエリエンジンがすべての行でインデックスからテーブルへのルックアップを行う必要がある場合、インデックスを使用するオプティマイザーにはほとんど意味がありません。

于 2013-01-01T07:28:54.103 に答える