web-dev-qa-db-ja.com

MYSQLのLIMITオフセットが高いとクエリが遅くなるのはなぜですか?

簡単に言えば、1600万件を超えるレコード(サイズが2GB)のテーブル。 ORDER BY * primary_key *を使用する場合、SELECTでのLIMITオフセットが大きいほど、クエリが遅くなります

そう

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 

はるかに少ない

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 

それは30レコードのみを注文し、いずれにしても同じです。したがって、ORDER BYのオーバーヘッドではありません。
現在、最新の30行をフェッチする場合、約180秒かかります。その単純なクエリをどのように最適化できますか?

151
Rahman

クエリは最初のOFFSET + LIMITレコードをカウントオフする必要があるため(そしてそれらのLIMITのみを取得する必要があるため)、オフセットが大きいとクエリの速度が低下するのが普通です。この値が高いほど、クエリの実行時間が長くなります。

クエリはOFFSETに直接移動できません。最初に、レコードの長さが異なる可能性があり、2番目に、削除されたレコードとの間にギャップがある可能性があるためです。途中で各レコードをチェックしてカウントする必要があります。

idMyISAMテーブルのPRIMARY KEYであると仮定すると、次のトリックを使用して高速化できます。

SELECT  t.*
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                id
        LIMIT 10000, 30
        ) q
JOIN    mytable t
ON      t.id = q.id

この記事を参照してください:

174
Quassnoi

私もまったく同じ問題を抱えていました。 30個の特定のセットではなく、このデータを大量に収集したいという事実を考えると、おそらくループを実行し、オフセットを30増やします。

したがって、代わりにできることは:

  1. 一連のデータの最後のIDを保持します(30)(例:lastId = 530)
  2. 条件を追加しますWHERE id > lastId limit 0,30

したがって、常にゼロオフセットを設定できます。パフォーマンスの改善に驚くことでしょう。

190
Nikos Kyr

MySQLは、そのようにパック/順序付けされている(または1から10000の連続値を持つ)と想定できないため、10000番目のレコード(または、提案として80000番目のバイト)に直接移動できません。実際にはそのように見えるかもしれませんが、MySQLは、ホール/ギャップ/削除されたIDがないと想定することはできません。

したがって、ボブが指摘したように、MySQLは返される30を見つける前に10000行をフェッチする(またはidのインデックスの10000番目のエントリをトラバースする)必要があります。

EDIT:私のポイントを説明するために

ただし、

SELECT * FROM large ORDER BY id LIMIT 10000, 30 

slow(er)

SELECT * FROM large WHERE id >  10000 ORDER BY id LIMIT 30 

fast(er)であり、ids(つまりギャップ)が欠落していなければ同じ結果を返します。

17
Riedsio

SELECTクエリORDER BY id LIMIT X、Yを最適化する興味深い例を見つけました。私は3500万の行があるので、行の範囲を見つけるのに2分ほどかかりました。

ここにトリックがあります:

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;

取得した最後のIDを持つWHEREを配置するだけで、パフォーマンスが大幅に向上します。私にとっては2分から1秒でした:)

他の興味深いコツ: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

文字列でも動作します

7
sym

2つのクエリの時間のかかる部分は、テーブルから行を取得することです。論理的に言えば、LIMIT 0, 30バージョンでは、30行のみを取得する必要があります。 LIMIT 10000, 30バージョンでは、10000行が評価され、30行が返されます。データ読み取りプロセスで最適化を行うことができますが、次のことを考慮してください。

クエリにWHERE句がある場合はどうなりますか?エンジンは、条件を満たすすべての行を返し、データを並べ替えて、最終的に30行を取得する必要があります。

また、行がORDER BYシーケンスで処理されない場合も考慮してください。どの行を返すかを決定するには、すべての適格な行をソートする必要があります。

5
bobs