簡単に言えば、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秒かかります。その単純なクエリをどのように最適化できますか?
クエリは最初のOFFSET + LIMIT
レコードをカウントオフする必要があるため(そしてそれらのLIMIT
のみを取得する必要があるため)、オフセットが大きいとクエリの速度が低下するのが普通です。この値が高いほど、クエリの実行時間が長くなります。
クエリはOFFSET
に直接移動できません。最初に、レコードの長さが異なる可能性があり、2番目に、削除されたレコードとの間にギャップがある可能性があるためです。途中で各レコードをチェックしてカウントする必要があります。
id
がMyISAM
テーブルのPRIMARY KEY
であると仮定すると、次のトリックを使用して高速化できます。
SELECT t.*
FROM (
SELECT id
FROM mytable
ORDER BY
id
LIMIT 10000, 30
) q
JOIN mytable t
ON t.id = q.id
この記事を参照してください:
私もまったく同じ問題を抱えていました。 30個の特定のセットではなく、このデータを大量に収集したいという事実を考えると、おそらくループを実行し、オフセットを30増やします。
したがって、代わりにできることは:
WHERE id > lastId limit 0,30
したがって、常にゼロオフセットを設定できます。パフォーマンスの改善に驚くことでしょう。
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)であり、id
s(つまりギャップ)が欠落していなければ同じ結果を返します。
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/
文字列でも動作します
2つのクエリの時間のかかる部分は、テーブルから行を取得することです。論理的に言えば、LIMIT 0, 30
バージョンでは、30行のみを取得する必要があります。 LIMIT 10000, 30
バージョンでは、10000行が評価され、30行が返されます。データ読み取りプロセスで最適化を行うことができますが、次のことを考慮してください。
クエリにWHERE句がある場合はどうなりますか?エンジンは、条件を満たすすべての行を返し、データを並べ替えて、最終的に30行を取得する必要があります。
また、行がORDER BYシーケンスで処理されない場合も考慮してください。どの行を返すかを決定するには、すべての適格な行をソートする必要があります。