”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 释放 MongoDB:为什么基于游标的分页每次都优于基于偏移量的分页!

释放 MongoDB:为什么基于游标的分页每次都优于基于偏移量的分页!

发布于2024-11-07
浏览:830

分页在处理大型数据集时是任何数据库操作的关键部分。它允许您将数据分割成可管理的块,从而更容易浏览、处理和显示。 MongoDB 提供了两种常见的分页方法:基于偏移量和基于游标。虽然这两种方法具有相同的目的,但它们在性能和可用性方面显着不同,尤其是随着数据集的增长。

让我们深入研究这两种方法,看看为什么基于光标的分页通常优于基于偏移量的分页。

1. 基于偏移量的分页

基于偏移量的分页很简单。它检索从给定偏移量开始的特定数量的记录。例如,第一页可能检索记录 0-9,第二页检索记录 10-19,依此类推。

但是,这种方法有一个显着的缺点:当您移动到更高的页面时,查询会变得更慢。这是因为数据库需要跳过前几页的记录,这涉及到扫描它们。

以下是基于偏移量的分页代码:

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 或时间戳)来对记录进行分页。它不是跳过一定数量的记录,而是使用最后检索到的记录作为获取下一组记录的参考点。

这种方法效率更高,因为它避免了扫描当前页之前的记录。因此,无论您深入数据集多深,查询时间都保持一致。

以下是基于光标的分页代码:

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

在此示例中,lastDocumentId 是上一页中最后一个文档的 ID。当查询下一页时,数据库会获取ID大于该值的文档,确保无缝过渡到下一组记录。

3. 性能比较

让我们看看这两种方法在大型数据集上的表现如何。

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

在性能测试中,你会注意到基于偏移的分页需要更长作为页码增加,而光标基于的分页保持一致,使其成为大型数据集的更好选择。这个例子也展示了索引的威力。尝试删除索引然后查看结果!

为什么索引很重要

如果没有索引,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如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 使用纯 Javascript 只需几行即可实现飞向购物车的动画。
    使用纯 Javascript 只需几行即可实现飞向购物车的动画。
    最近,我偶然发现了一个旧教程,展示了使用 jQuery 实现飞行到购物车的动画。我想通过使用纯 JavaScript 实现相同的效果来挑战自己。 我创建了一个包含产品和购物车图标的简单布局。样式并不重要,所以我们不会在这里讨论它。 诀窍是克隆产品图像,将其添加到产品元素之前。然后计算克隆图像和购物车...
    编程 发布于2024-11-08
  • Bokeh 是一个有趣的 Python 数据可视化数据工具
    Bokeh 是一个有趣的 Python 数据可视化数据工具
    数据可视化在解释大量信息方面发挥着关键作用。 Bokeh 等工具已成为构建交互式仪表板和报告的流行解决方案。每个工具都具有独特的优势,具体取决于您项目的复杂性和您首选的编程语言。在本文中,我们将深入研究每个工具,然后重点关注 Bokeh,包括实践示例和云中的部署。 以便... 什么是散景? Boke...
    编程 发布于2024-11-08
  • django-components v 模板现在与 Vue 或 React 相当
    django-components v 模板现在与 Vue 或 React 相当
    嘿,我是 Juro,我是 django-components 的维护者之一。在 v0.90-0.94 版本中,我们添加了一些功能,使模板中的组件使用更加灵活,类似于 JSX / Vue。 (此信息已经有点过时了(一个月前发布;最新的是 v0.101),因为我正忙着添加对 JS / CSS 变量、Ty...
    编程 发布于2024-11-08
  • 在 GitHub-echo 中实现 TOML 配置支持
    在 GitHub-echo 中实现 TOML 配置支持
    介绍 最近,我有机会通过添加对 TOML 配置文件的支持来增强 github-echo 命令行工具。此功能允许用户在 .github-echo-config.toml 文件中设置持久默认选项,从而减少每次使用该工具时手动输入重复配置的需要。在这篇文章中,我将向您介绍我在该功能上的经...
    编程 发布于2024-11-08
  • 如何使用 SimpleXML 和 DOMDocument 删除 XPath 节点?
    如何使用 SimpleXML 和 DOMDocument 删除 XPath 节点?
    SimpleXML:删除 XPath 节点在本文中,我们将探讨如何使用以下方法有效地从 XML 文档中删除父节点: SimpleXML 和 XPath。了解 SimpleXML限制提供的代码尝试使用 SimpleXML 在通过 XPath 找到父节点后删除它。然而,SimpleXML 的 unset...
    编程 发布于2024-11-08
  • 创建一个 React Hook 以任意角度旋转图像
    创建一个 React Hook 以任意角度旋转图像
    在Web开发中,您可能需要旋转图像,这在CSS中很容易做到。像这样的简单代码变换:rotate(90deg);。但是如果我们想用 JS 来做呢? TLDR 将图像绘制到浏览器环境中的画布上并旋转它。但在此之前,我们需要做一些数学运算来保持原始图像的长宽比。 核 假设我...
    编程 发布于2024-11-08
  • Lithe 中间件:它是如何工作的以及如何创建自己的中间件
    Lithe 中间件:它是如何工作的以及如何创建自己的中间件
    中间件提供了一种方便的机制来检查和过滤进入应用程序的 HTTP 请求。 例如,Lithe 包含检查用户是否经过身份验证的中间件。如果没有,中间件会将用户重定向到登录屏幕。如果用户通过身份验证,中间件将允许请求继续。 中间件如何在 Lithe 中工作 在 Lithe 中,中间件是能够访...
    编程 发布于2024-11-08
  • 如何在 JavaScript 中创建具有重复元素的数组?
    如何在 JavaScript 中创建具有重复元素的数组?
    JavaScript 中重复元素的数组创建具有多次重复的相同元素的数组在各种编程场景中至关重要。在 Python 中,这可以通过列表乘法来实现,如 [2] * 5 中所示。但是,此功能在 JavaScript 数组中不能直接使用。自定义函数方法为了满足这种需求,一种方法是创建自定义函数,例如问题中提...
    编程 发布于2024-11-08
  • ## MySQL 中的 LIKE 与 LOCATE:哪个运算符是性能之王?
    ## MySQL 中的 LIKE 与 LOCATE:哪个运算符是性能之王?
    MySQL LIKE 与 LOCATE 性能比较在 MySQL 中查找数据时,你可能想知道 LIKE 和 LOCATE 哪个运算符效率更高?本文探讨了这两个运算符之间的性能差异。在典型的使用场景中,LIKE 比 LOCATE 稍快。这主要是因为 LIKE 不像 LOCATE 那样执行与 0 的额外比...
    编程 发布于2024-11-08
  • 如何使用 PHP 更新多个 MySQL 行的表单数据?
    如何使用 PHP 更新多个 MySQL 行的表单数据?
    使用表单数据更新多个 MySQL 行在 Web 开发中,通常有一个表单,用户可以在其中编辑数据库中的记录。一种常见的情况是使用修改后的数据更新同一个表中的多行。这可以使用 PHP 和 MySQL 来实现。表单结构和数据检索初始表单负责呈现要编辑的数据。在此示例中,表单从数据库中检索具有特定 GALL...
    编程 发布于2024-11-08
  • 为什么我不能在 Go 中将 []byte 分配给字符串?
    为什么我不能在 Go 中将 []byte 分配给字符串?
    了解字节分配错误:无法将 []byte 分配给字符串在尝试读取文件夹中的文件时,遇到了错误尝试读取文件内容时,“无法在多重赋值中将 []byte 分配给 z(字符串类型)”。让我们深入研究一下这个错误背后的原因。理解多重赋值当在一行中为多个变量赋值时,如代码所示:z, err := ioutil.R...
    编程 发布于2024-11-08
  • 如何使用 React 和 Typescript 创建自定义表格组件(第 2 部分)
    如何使用 React 和 Typescript 创建自定义表格组件(第 2 部分)
    介绍 耶! ?您已经完成了这个由两部分组成的系列的最后一部分!如果您还没有查看第 1 部分,请先停在此处并完成第 1 部分。别担心,我们会等你回来! ? 在第 1 部分中,我们构建了 CustomTable 组件。您可以在这里看到它的实际效果。 在第二部分中,我们将扩展该组件以添加...
    编程 发布于2024-11-08
  • 使用 TypeScript 和 ioredis 在 Node.js 中构建高性能缓存管理器
    使用 TypeScript 和 ioredis 在 Node.js 中构建高性能缓存管理器
    使用基于 ioredis 构建的多功能、易于使用的缓存管理器来提升 Node.js 应用程序的性能。简化缓存、优化效率并简化操作。 我根据自己的需求开发了一个基于 ioredis 的类,重点关注易用性和性能。它包括 TypeScript 支持,旨在实现简单使用和高效操作。它仍然可以进一步改进和优化,...
    编程 发布于2024-11-08
  • 超类引用和子类对象
    超类引用和子类对象
    Java 是一种强类型语言。 标准转换和自动升级适用于原始类型。 严格执行类型兼容性。 通常,一个类的引用变量不能引用另一类的对象。 即使类 X 和 Y 在结构上相同,也不可能将 X 的引用分配给 Y 的对象,因为类型不同。 一般来说,对象引用变量只能引用其类型的对象。 类型强加的例外是超类的引用...
    编程 发布于2024-11-08
  • Flexbox 中的 flex-grow 和 width 有何不同?
    Flexbox 中的 flex-grow 和 width 有何不同?
    Flexbox中flex-grow和width的区别Flexbox提供了两种在元素之间分配空间的主要方法:flex-grow和width。了解这些属性之间的区别对于有效使用 Flexbox 至关重要。Flex-grow 与 widthFlex-grow 是一个无量纲属性,定义元素的大小扩展以填充沿主...
    编程 发布于2024-11-08

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3