ページングの通常の方法は、オフセットまたはページ番号によるものです。次のようなリクエストを行います。
GET /api/products?page=10 {"items": [...100 products]}
そしてこれ:
GET /api/products?page=11 {"items": [...another 100 products]}
シンプルな変位の場合には、それが判明
?offset=1000
し、
?offset=1100
プロファイルのみで、同じ卵を- 。ここでは、タイプのSQLクエリに直接移動するか
OFFSET 1000 LIMIT 100
、ページサイズ(value
LIMIT
)を掛けます 。すべてのデータベースがこれらの1000行をスキップする必要があるため、これはとにかく最適なソリューションではありません。そして、それらをスキップするには、それらを識別する必要があります。 PostgreSQL、ElasticSearch、MongoDBのいずれであるかは関係ありません。順序付け、再計算、および破棄する必要があります。
これは不要な作業です。ただし、この設計は実装が簡単であるため、何度も繰り返されます。APIをデータベース要求に直接マップします。
それでは何をすべきでしょうか?データベースがどのように機能するかを見ることができました!それらにはカーソルの概念があります-それは文字列へのポインタです。したがって、データベースに「この後100行返してください」と伝えることができます 。また、このようなクエリは、インデックスのあるフィールドで行を識別する可能性が高いため、データベースにとってはるかに便利です。そして、これらの行をフェッチしてスキップする必要はありません。すぐそばを歩きます。
例:
GET /api/products {"items": [...100 products], "cursor": "qWe"}
APIは(不透明な)文字列を返します。これを使用して次のページを取得できます。
GET /api/products?cursor=qWe {"items": [...100 products], "cursor": "qWr"}
実装に関しては、多くのオプションがあります。通常、製品IDなどのクエリ条件があります。この場合、いくつかのリバーシブルアルゴリズム(ハッシュ識別子など)でエンコードします 。また、カーソル付きのクエリを受信すると、それをデコードして、のようなクエリを生成します
WHERE id > :cursor LIMIT 100
。
小さなパフォーマンスの比較。これがオフセットの結果です: そしてこれが操作の結果です : 数桁の違い!もちろん、実際の数は、テーブルのサイズ、フィルター、およびストレージの実装によって異なります。ここに素晴らしい記事があり ます
=# explain analyze select id from product offset 10000 limit 100;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Limit (cost=1114.26..1125.40 rows=100 width=4) (actual time=39.431..39.561 rows=100 loops=1)
-> Seq Scan on product (cost=0.00..1274406.22 rows=11437243 width=4) (actual time=0.015..39.123 rows=10100 loops=1)
Planning Time: 0.117 ms
Execution Time: 39.589 ms
where
=# explain analyze select id from product where id > 10000 limit 100;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..11.40 rows=100 width=4) (actual time=0.016..0.067 rows=100 loops=1)
-> Seq Scan on product (cost=0.00..1302999.32 rows=11429082 width=4) (actual time=0.015..0.052 rows=100 loops=1)
Filter: (id > 10000)
Planning Time: 0.164 ms
Execution Time: 0.094 ms
詳細な技術情報については、パフォーマンスの比較についてスライド42を参照してください。
もちろん、IDで製品を照会する人は誰もいません。通常、製品は何らかの関連性について照会されます(次に、重要なパラメーターとしてIDが照会され ます)。現実の世界では、ソリューションを選択するには、特定のデータを調べる必要があります。リクエストは識別子で並べ替えることができます(単調に増加するため)。将来の購入のリストからのアイテムも、この方法で並べ替えることができます-リストがコンパイルされたときまでに。私たちの場合、製品はそのようなカーソルを自然にサポートするElasticSearchからロードされます。
欠点は、ステートレスAPIを使用して前のページのリンクを作成できないことです。ユーザーのページネーションの場合、この問題を回避する方法はありません。したがって、前/次のページのボタンと「10ページに直接移動する」が重要な場合は、古い方法を使用する必要があります。ただし、他の場合、特にページネーションが非常に深い非常に大きなテーブルでは、byカーソル方式を使用するとパフォーマンスが大幅に向上します。