Heim  >  Artikel  >  Web-Frontend  >  MongoDB entfesseln: Warum die Cursor-basierte Paginierung jedes Mal die Offset-basierte Paginierung übertrifft!

MongoDB entfesseln: Warum die Cursor-basierte Paginierung jedes Mal die Offset-basierte Paginierung übertrifft!

王林
王林Original
2024-09-04 22:43:021055Durchsuche

Paginierung ist ein wichtiger Bestandteil jedes Datenbankbetriebs, wenn es um die Verarbeitung großer Datenmengen geht. Es ermöglicht Ihnen, Daten in überschaubare Blöcke aufzuteilen und so das Durchsuchen, Verarbeiten und Anzeigen zu vereinfachen. MongoDB bietet zwei gängige Paginierungsmethoden: Offset-basiert und Cursor-basiert. Obwohl beide Methoden denselben Zweck erfüllen, unterscheiden sie sich erheblich in der Leistung und Benutzerfreundlichkeit, insbesondere wenn der Datensatz wächst.

Lassen Sie uns in die beiden Ansätze eintauchen und sehen, warum die Cursor-basierte Paginierung oft die Offset-basierte Paginierung übertrifft.

1. Offsetbasierte Paginierung

Offsetbasierte Paginierung ist unkompliziert. Es ruft eine bestimmte Anzahl von Datensätzen ab einem bestimmten Offset ab. Beispielsweise könnte die erste Seite die Datensätze 0–9 abrufen, die zweite Seite die Datensätze 10–19 und so weiter.

Diese Methode hat jedoch einen erheblichen Nachteil: Wenn Sie zu höheren Seiten wechseln, wird die Abfrage langsamer. Dies liegt daran, dass die Datenbank die Datensätze der vorherigen Seiten überspringen muss, was ein Durchsuchen dieser Seiten erfordert.

Hier ist der Code für die offsetbasierte Paginierung:

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. Cursorbasierte Paginierung

Cursorbasierte Paginierung, auch Keyset-Paginierung genannt, basiert auf einer eindeutigen Kennung (z. B. einer ID oder einem Zeitstempel), um durch die Datensätze zu paginieren. Anstatt eine bestimmte Anzahl von Datensätzen zu überspringen, wird der zuletzt abgerufene Datensatz als Referenzpunkt für den Abruf des nächsten Satzes verwendet.

Dieser Ansatz ist effizienter, da er die Notwendigkeit vermeidet, die Datensätze vor der aktuellen Seite zu scannen. Dadurch bleibt die Abfragezeit konsistent, unabhängig davon, wie tief Sie in den Datensatz vordringen.

Hier ist der Code für die Cursor-basierte Paginierung:

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);
}

In diesem Beispiel ist lastDocumentId die ID des letzten Dokuments der vorherigen Seite. Bei der Abfrage der nächsten Seite ruft die Datenbank Dokumente mit einer ID ab, die größer als dieser Wert ist, und gewährleistet so einen nahtlosen Übergang zum nächsten Satz von Datensätzen.

3. Leistungsvergleich

Sehen wir uns an, wie diese beiden Methoden bei einem großen Datensatz funktionieren.

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();
}

Image description

Im Leistungstest werden Sie feststellen, dass die offsetbasierte Paginierung mit zunehmender Seitenzahl länger dauert, während der Cursor -basierte Paginierung bleibt konsistent und ist daher die bessere Wahl für große Datensätze. Dieses Beispiel zeigt auch die Leistungsfähigkeit der Indizierung. Versuchen Sie, den Index zu entfernen und sehen Sie sich dann auch das Ergebnis an!

Warum die Indizierung wichtig ist

Ohne einen Index müsste MongoDB einen Sammlungsscan durchführen, was bedeutet, dass jedes Dokument in der Sammlung durchsucht werden muss, um die relevanten Daten zu finden. Dies ist ineffizient, insbesondere wenn Ihr Datensatz wächst. Mithilfe von Indizes kann MongoDB effizient die Dokumente finden, die Ihren Abfragebedingungen entsprechen, wodurch die Abfrageleistung erheblich beschleunigt wird.

Im Kontext der Cursor-basierten Paginierung stellt der Index sicher, dass das Abrufen des nächsten Satzes von Dokumenten (basierend auf documentId) schnell erfolgt und die Leistung nicht abnimmt, wenn der Sammlung weitere Dokumente hinzugefügt werden.

Fazit

Obwohl die Offset-basierte Paginierung einfach zu implementieren ist, kann sie bei großen Datensätzen aufgrund der Notwendigkeit, Datensätze zu durchsuchen, ineffizient werden. Die Cursor-basierte Paginierung hingegen bietet eine skalierbarere Lösung und sorgt für eine gleichbleibende Leistung unabhängig von der Datensatzgröße. Wenn Sie mit großen Sammlungen in MongoDB arbeiten, lohnt es sich, die Cursor-basierte Paginierung für ein reibungsloseres und schnelleres Erlebnis in Betracht zu ziehen.

Hier ist die vollständige index.js, die Sie lokal ausführen können:

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 < 2000000; i++) {
      bulkOps.push({
        insertOne: {
          documentId: i,
          name: `Record-${i}`,
          value: Math.random() * 1000,
        },
      });

      // Execute every 10000 operations and reinitialize
      if (bulkOps.length === 10000) {
        await collection.bulkWrite(bulkOps);
        bulkOps = [];
      }
    }

    if (bulkOps.length > 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();

Das obige ist der detaillierte Inhalt vonMongoDB entfesseln: Warum die Cursor-basierte Paginierung jedes Mal die Offset-basierte Paginierung übertrifft!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Nicht-NestJS-EreignisseNächster Artikel:Nicht-NestJS-Ereignisse