2

約300万件のレコードがある単純なテーブルがあります。必要なインデックスを作成しました。インデックスPRIMARYも強制しますが、それでも機能しません。インデックスを使用してこの行を実行する代わりに、ほぼすべての300万行を検索します(record_idはINT自動インクリメントです)。

EXPLAIN SELECT record_id
FROM myrecords
FORCE INDEX (
PRIMARY )
ORDER BY record_id ASC
LIMIT 2955900 , 300

id  select_type     table     type  possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE          myrecords index NULL            PRIMARY 4           NULL    2956200 Using index

インデックスは

Keyname Type    Unique  Packed  Column      Cardinality Collation   Null
PRIMARY BTREE   Yes     No      record_id   2956742     A           No  

このFORCEDインデックスが正しく使用されていない理由を知りたいです。

ASCとDESCの両方が試行されたインデックス「プライマリ」を強制しない場合、結果は同じです。テーブルが修復され、最適化され、分析されました。運がない。

クエリの実行には1分以上かかります。

私が期待したこと:その列はインデックス付けされているので、クエリは300行だけを処理する必要があります。最初のコード形式のブロックでわかるように、それらのほぼすべてが300万ではありません(少し右にスクロールします)

4

1 に答える 1

7

インデックス ルックアップは、位置ではなくで行われます。インデックスは値 2955900 を検索できますが、それを求めているわけではありません。テーブルの 2955900 行目のオフセットからクエリを開始するよう求めています。

オプティマイザーは、すべての主キー値が連続しているとは想定できません。したがって、2955900 番目の行の値はそれよりもはるかに高い可能性があります。

主キーの値が連続している場合でも、たとえば 45% の行のみが一致する WHERE 条件がある場合があります。この場合、2955900 番目の行の ID 値は、ID 値 2955900 をはるかに超えています

つまり、id 値 2955900 のインデックス ルックアップでは、2955900 番目の行は配信されません。

そのため、MySQL は制限のオフセットにインデックスを使用できません。オフセット+制限行に達するまで、行をスキャンしてカウントする必要があります。

MySQL にはLIMITに関連する最適化がありますが、返される行数に達したらテーブル スキャンを停止することが重要です。オプティマイザーは、テーブル全体をスキャンする必要があると予想される EXPLAIN プランで報告する場合があります。

FORCE INDEXについてよく誤解されるのは、インデックスの使用を強制するというものです。:-) 実際、クエリがインデックスを使用できない場合 (または利用可能なインデックスがこのクエリに何の利点もない場合)、FORCE INDEX は効果がありません。


あなたのコメントについて:

ページネーションは、データ駆動型 Web アプリケーションのよくある悩みの種です。この機能がいかに一般的であるかに関わらず、最適化は容易ではありません。いくつかのヒントを次に示します。

  • なぜオフセット 2955900 でクエリを実行しているのですか? ユーザーがそんなに多くのページをふるいにかけることを本当に期待していますか? ほとんどのユーザーは数ページであきらめます (正確な数は、アプリケーションとデータの種類によって異なります)。

  • クエリの数を減らします。ページネーション機能は、最初のページのみをユーザーに表示する場合でも、最初の 5 ~ 10 ページを取得できます。ユーザーが数ページ進むことを想定して、他のページをキャッシュします。キャッシュされた一連のページを通過した場合にのみ、アプリは別のクエリを実行する必要があります。クライアントのブラウザーで Javascript の 10 ページすべてをキャッシュすることもできるため、"次へ" をクリックするのは瞬時です (少なくとも最初の数ページでは)。

  • ユーザー インターフェースに「最後」ボタンを配置しないでください。ユーザーは好奇心からボタンをクリックするからです。Google には「次へ」ボタンがありますが、「最後」ボタンはありません。そのため、UI 自体が、ユーザーがオフセットの多い非効率的なクエリを実行するのを思いとどまらせます。

  • ユーザーが一度に 1 ページずつ進む場合は、前のページで返された最大の id 値を、次のページのクエリの WHERE 句で使用します。つまり、FORCE INDEX ヒントがなくても、以下はインデックスを使用します。

    SELECT * FROM thistable WHERE id > 544 LIMIT 20
    
于 2013-02-28T20:23:27.270 に答える