「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > MongoDB を解き放つ: カーソルベースのページネーションが常にオフセットベースのページネーションよりも優れている理由!

MongoDB を解き放つ: カーソルベースのページネーションが常にオフセットベースのページネーションよりも優れている理由!

2024 年 11 月 7 日に公開
ブラウズ:929

ページネーションは、大規模なデータセットを扱う場合のデータベース操作の重要な部分です。データを管理可能なチャンクに分割できるため、参照、処理、表示が容易になります。 MongoDB は、オフセット ベースとカーソル ベースという 2 つの一般的なページネーション メソッドを提供します。どちらの方法も同じ目的を果たしますが、特にデータセットが大きくなるにつれて、パフォーマンスと使いやすさの点で大きく異なります

2 つのアプローチを詳しく見て、なぜカーソル ベースのページネーションがオフセット ベースのページネーションよりも優れているのかを見てみましょう。

1.

オフセットベースのページネーション

オフセットベースのページネーションは簡単です。指定されたオフセットから開始して特定の数のレコードを取得します。たとえば、最初のページはレコード 0 ~ 9 を取得し、2 番目のページはレコード 10 ~ 19 を取得する、というようになります。

ただし、この方法には重大な欠点があります。上位のページに移動すると、クエリが遅くなります。これは、データベースが前のページのレコードをスキップする必要があり、レコードをスキャンする必要があるためです。

オフセットベースのページネーションのコードは次のとおりです:


非同期関数 offset_based_pa​​gination(params) { const { ページ = 5、制限 = 100 } = パラメータ; const スキップ = (ページ - 1) * 制限; const results = await collection.find({}).skip(skip).limit(limit).toArray(); console.log(`オフセットベースのページネーション (ページ ${page}):`, results.length, "ページ", ページ, "スキップ", スキップ, "制限", 制限); }
async function offset_based_pagination(params) {
  const { page = 5, limit = 100 } = params;
  const skip = (page - 1) * limit;
  const results = await collection.find({}).skip(skip).limit(limit).toArray();
  console.log(`Offset-based pagination (Page ${page}):`, results.length, "page", page, "skip", skip, "limit", limit);
}
2.

カーソルベースのページネーション

カーソルベースのページネーションは、キーセット ページネーションとも呼ばれ、一意の識別子 (ID やタイムスタンプなど) に基づいてレコードのページネーションを行います。特定の数のレコードをスキップする代わりに、最後に取得したレコードを次のセットをフェッチするための参照ポイントとして使用します。

このアプローチは、現在のページの前にレコードをスキャンする必要がなくなるため、より効率的です。その結果、データセットをどれだけ深く掘り下げても、クエリ時間は一貫したままになります。

カーソルベースのページネーションのコードは次のとおりです:


非同期関数cursor_based_pa​​gination(params) { const { lastDocumentId、limit = 100 } = params; const クエリ = lastDocumentId ? { documentId: { $gt: lastDocumentId } } : {}; const results = コレクションを待つ .find(クエリ) .sort({ ドキュメント ID: 1 }) .limit(制限) .toArray(); console.log("カーソルベースのページネーション:", results.length); }
async function offset_based_pagination(params) {
  const { page = 5, limit = 100 } = params;
  const skip = (page - 1) * limit;
  const results = await collection.find({}).skip(skip).limit(limit).toArray();
  console.log(`Offset-based pagination (Page ${page}):`, results.length, "page", page, "skip", skip, "limit", limit);
}
この例では、

lastDocumentId は、前のページの最後のドキュメントの ID です。次のページをクエリする場合、データベースはこの値より大きい ID を持つドキュメントをフェッチし、次のレコード セットへのシームレスな移行を保証します。

3.

パフォーマンスの比較

これら 2 つのメソッドが大規模なデータセットでどのように実行されるかを見てみましょう。


非同期関数 testMongoDB() { console.time("MongoDB 挿入時間:"); insertMongoDBRecords() を待ちます; console.timeEnd("MongoDB 挿入時刻:"); // documentId フィールドにインデックスを作成します await collection.createIndex({ documentId: 1 }); console.log("documentId フィールドに作成されたインデックス"); console.time("オフセットベースのページネーション時間:"); await offset_based_pa​​gination({ ページ: 2, 制限: 250000 }); console.timeEnd("オフセットベースのページネーション時間:"); console.time("カーソルベースのページネーション時間:"); await Cursor_based_pa​​gination({ lastDocumentId: 170000, limit: 250000 }); console.timeEnd("カーソルベースのページネーション時間:"); client.close() を待ちます。 }
async function offset_based_pagination(params) {
  const { page = 5, limit = 100 } = params;
  const skip = (page - 1) * limit;
  const results = await collection.find({}).skip(skip).limit(limit).toArray();
  console.log(`Offset-based pagination (Page ${page}):`, results.length, "page", page, "skip", skip, "limit", limit);
}

Image description

パフォーマンス テストでは、

オフセット ベースのページネーションでは、ページ番号が増加するにつれて長くなるがかかりますが、カーソルは カーソル-based ページネーション 一貫性が維持される ため、 大規模な データセットに適しています。この例では、インデックス作成の力も示しています。インデックスを削除して、結果も確認してみてください!

インデックス作成が重要な理由

インデックスがないと、MongoDB はコレクション スキャンを実行する必要があります。つまり、コレクション内の各ドキュメントを調べて関連データを見つける必要があります。これは、特にデータセットが増大する場合には非効率的です。インデックスを使用すると、MongoDB はクエリ条件に一致するドキュメントを効率的に見つけられるようになり、クエリのパフォーマンスが大幅に高速化されます。

カーソル ベースのページネーションのコンテキストでは、インデックスにより、次のドキュメント セット (documentId に基づく) のフェッチが迅速になり、コレクションにドキュメントが追加されてもパフォーマンスが低下しないことが保証されます。

結論

オフセットベースのページネーションは実装が簡単ですが、大規模なデータセットではレコードをスキャンする必要があるため非効率になる可能性があります。一方、カーソルベースのページネーションは、よりスケーラブルなソリューションを提供し、データセットのサイズに関係なくパフォーマンスの一貫性を保ちます。 MongoDB で大規模なコレクションを操作している場合は、よりスムーズで高速なエクスペリエンスを実現するために、カーソルベースのページネーションを検討する価値があります。

ローカルで実行できる完全なindex.jsは次のとおりです:

const { MongoClient } = require("mongodb");
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
client.connect();
const db = client.db("testdb");
const collection = db.collection("testCollection");

async function insertMongoDBRecords() {
  try {
    let bulkOps = [];

    for (let i = 0; i  0) {
      await collection.bulkWrite(bulkOps);
      console.log("? Inserted records till now -> ", bulkOps.length);
    }

    console.log("MongoDB Insertion Completed");
  } catch (err) {
    console.error("Error in inserting records", err);
  }
}

async function offset_based_pagination(params) {
  const { page = 5, limit = 100 } = params;
  const skip = (page - 1) * limit;
  const results = await collection.find({}).skip(skip).limit(limit).toArray();
  console.log(`Offset-based pagination (Page ${page}):`, results.length, "page", page, "skip", skip, "limit", limit);
}

async function cursor_based_pagination(params) {
  const { lastDocumentId, limit = 100 } = params;
  const query = lastDocumentId ? { documentId: { $gt: lastDocumentId } } : {};
  const results = await collection
    .find(query)
    .sort({ documentId: 1 })
    .limit(limit)
    .toArray();
  console.log("Cursor-based pagination:", results.length);
}

async function testMongoDB() {
  console.time("MongoDB Insert Time:");
  await insertMongoDBRecords();
  console.timeEnd("MongoDB Insert Time:");

  // Create an index on the documentId field
  await collection.createIndex({ documentId: 1 });
  console.log("Index created on documentId field");

  console.time("Offset-based pagination Time:");
  await offset_based_pagination({ page: 2, limit: 250000 });
  console.timeEnd("Offset-based pagination Time:");

  console.time("Cursor-based pagination Time:");
  await cursor_based_pagination({ lastDocumentId: 170000, limit: 250000 });
  console.timeEnd("Cursor-based pagination Time:");

  await client.close();
}

testMongoDB();

リリースステートメント この記事は次の場所に転載されています: https://dev.to/franklinthaker/unleashing-mongodb-why-cursor-based-pagination-outperforms-offset-based-pagination-every-time-4o30?1 侵害がある場合は、 Study_golang@163 .comdelete に連絡してください
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3