大規模なデータセットの場合、OFFSET
を使用したページ付けは速度が遅く、ページ付けの最適な方法ではないことがわかっています。ページ付けを行うより優れた方法は、カーソルを使用することです。これは行の一意の識別子であり、最後のカーソル位置から最後に中断したところからページ付けを継続する場所がわかります。
カーソルが自動インクリメントid
値である場合、実装はかなり簡単です。
SELECT * FROM users
WHERE id <= %cursor // cursor is the auto incrementing id, ex. 100000
ORDER BY id DESC
LIMIT %limit
確信が持てないのは、自動インクリメントするid
カーソルの代わりに、カーソルの一意の順次識別子がテーブル行のuuid
とcreated_at
だけである場合です。
確かにuuid
に基づいてクエリを実行してcreated_at
を取得し、<= created_at
であるすべてのusers
を選択することができますが、問題は、 users
テーブルの同じcreated_at
タイムスタンプ? uuid/created_at
カーソルの組み合わせに基づいてusers
tableにクエリを実行し、正しいデータセットを取得する方法を教えてください(自動インクリメントid
を使用しているかのように)?繰り返しますが、created_at
は重複する可能性があるため、唯一の一意のフィールドはuuid
ですが、それらの組み合わせは行ごとに一意になります。
私はあなたが尋ねたものに答えますが、最初に、なぜあなたがそれをしたいのか、私には理解できないと言っておきます。自動増分IDは、このタスクに非常に適しています。ただし、並べ替えにIDに依存することは悪い習慣であるため、timestamp列も使用することは適切です。どうして?たとえば、Galeraクラスターを使用していてフェイルオーバーが発生している場合など、その順序が時系列ではない場合があるためです。
要求したことを行うには、まずこのインデックスを作成します。
ALTER TABLE users
ADD INDEX idx_created_at_uuid (created_at, uuid);
列の順序は重要です。逆にすると、インデックスは役に立ちません。
次のようなクエリを実行するだけです。
SELECT some_columns
FROM users
WHERE created_at <= x AND uuid = y
ORDER BY created_at DESC;
uuid
は、created_atが一意でないためにのみ必要です。 created_at
は最初の列ではありません。MySQLはすべての行を読み取り、それらをソートするためにそれらを一時テーブル(メモリ内またはディスク上の可能性があります)にコピーする必要があります。
IDを使用する場合は、上記のスニペットをそのまま使用しますが、uuid
をid
に置き換えます。
WHERE created_at <= x
AND ( created_at < x OR uuid < y )
ORDER BY created_at DESC,
uuid DESC
またはこれと同等のもの:
WHERE ( created_at < x
OR ( created_at = x OR uuid < y )
)
ORDER BY created_at DESC,
uuid DESC
この手法は、最初の列(created_at
)が重複する可能性があり、2番目の列が一意である(uuid
またはid
)列のペアに対して機能します。
そしてこれは必須です:
INDEX(created_at, uuid)
WHERE
の両方の部分はDESC
であることに注意してください。 ASC
とDESC
を混在させると、INDEX
の使いやすさが損なわれます。 (MySQL 8.0はそれを回避できます。)
また、これは、created_at
が重複しているときに行の順序を気にしないことを前提としていますが、consistentが必要であることにも注意してください。注文。 uuid
はランダムに見えますが、一貫性があることに注意してください。そうは言っても、id
(Galeraの有無にかかわらず)とuuid
は同じように機能します。
(UUIDは吸うが、それは 別の議論 である)
OFFSET
を使用しない改ページの詳細。