前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用桶模式进行分页——第一讲

如何使用桶模式进行分页——第一讲

作者头像
MongoDB中文社区
修改2019-09-27 18:33:53
1.4K0
修改2019-09-27 18:33:53
举报
文章被收录于专栏:MongoDB中文社区MongoDB中文社区
MongoDB中文社区(微信公众号:mongoing-mongoing)
MongoDB中文社区(微信公众号:mongoing-mongoing)

#数据模型

不知你是否注意过:查看页面时,随着页码的增加,翻页的速度也会随之变慢?应用程序设计人员虽然经常处理这个问题,但该问题依然存在。对此,有什么解决方案吗?我们可以使用一种灵活、易用的数据模型,MongoDB就是理想的解决方案,它提供强大的数据建模方法,使分页变得快速、高效。今天,我们就来探索在大量数据的前提下如何快速简单分页的问题。

首先,我们需要理解这个问题。如果一个完整的数据集不能适配在一个屏幕上显示,就必须采用分页方式。在需要设置“下一页”按钮前,大多数开发人员会将显示的条目数值限制为20、50或100。实现分页的最常用方式是在数据库级别上使用sort、skip和limit命令,但使用“skip和limit”命令存在一个问题:即随着页码的增加,页面加载速度为什么会变慢?这个问题很常见,它是由skip和limit的工作方式造成的。假设某个客户的股票交易网页每页显示1000条最新交易记录。查询history集合信息,就会生成相应的交易列表,方法如下图所示:

数据库使用索引 { customerId: 1, date: 1 } 找到了1,000个文档,并返回1,000个文档。每个文档都按照日期排序。这确实很简单。

下一页也采取类似的处理方式,唯一不同的是,我们用skip 1,000代替skip 0。数据库很容易就找到了2,000个文档并返回1,000个文档。等一下……数据库确实找到了2,000个文档吗?是的,它找到了2,000个文档并返回了1,000个文档。这就是skip和limit命令的工作方式。想象一下,我们要查看第5,000页的内容,我们会使用skip 5,000,000和limit 1,000。数据库必须找到5,001,000个文档并返回1,000个文档。难怪会花费那么长的时间!其实,有一个更好的方法。

对文档执行跳过(Skipping)命令很费时间,相反,不对文档执行跳过命令就不费时间了。还记得我们加载的第一个页面吗?我们取回1,000条结果,并准备将它们显示出来。我们必须循环访问这1,000个文档,每个文档都有一个日期。我们还可以很方便地按照日期进行排序。记住所显示文档的最后一个日期(例如,通过会话变量或查询字符串),我们就可以对查询语句做如下图所示的相应修改,并且不再需要跳过命令。

第二条查询语句就不包含跳过命令,同时有效使用了我们的索引。这真是一项改进!但我们仍然面临一个问题。

使用该方法,如要查看第5,000页的内容,速度确实快了很多,但我们还是没有办法从第1页直接跳转到第5,000页。为什么呢?这个方法对查询语句自身做了修改,使查找结果的过程缩短了。但它需要跟踪上一个页面的最后一个文档,以便对查询语句作出修改。要显示第5,000页上的文档,就需要加载第4,999页的最后一个文档,而这又需要加载第4,998页的最后一个文档,同理,又要加载第4,997页的最后一个文档,以此类推。

使用另一种方法的要点在于:如何在不需要事先加载之前所有数据的情况下加载所需的数值。这种解决方案需要跟踪所查看的最后一个文档,以便找到下一个文档集。只在我们不向用户提供跳转到指定页面的选项时,才使用这种方法。

有一种更好的方法:使用桶模式

首先简单介绍一下桶模式。桶模式最适用于列表中的事物彼此相似、且全部与某个中央实体相关的场合。捕获随时间变化的数据点就属于这类场合。而且,重要的是,大多数需要分页的数据集都能使用这种模式。

前文示例中处理的集合数据类似以下情况:

下面是使用桶模式处理的相同数据集:

使用桶模式,两个交易文档就凝缩成使用同一数组交易的一个单独文档。由于原始的设计有两个文档,这个数组包含两个对象。两个原始文档的重复字段凝缩为我们单一文档的根部(即customerId)。另外,单独字段作为history数组的一部分显示。

这种模式设计范式有很多优点,在此,我们只关注它在分页应用中所显示的优点。我们还要注意添加count字段。它代表了history数组中显示的交易数量。接下来,count字段将变得非常重要。

这些和分页操作都存在着哪些关系?采集历史信息的最有效方法就是根据显示需要存储信息。这正是MongoDB所擅长的。通过各种丰富、复杂的方式,帮助你按照需要恰当地存储数据。对于分页应用,按照需要,桶中的数据量可以是20、50、100等等。桶模式允许我们将每个页面都用单独的文档表示。

让我们用另一种方式思考这同一个概念。如果使用“skip和limit查找”的老方法显示页面,每一页都要从多个文档循环加载。每页如需显示20条交易,就需要反复20次移动光标,从服务器上提取20个文档。如果采用桶模式的方法进行分页,加载每一页只需要一个单独的文档,而这个单独文档就能生成整个页面!

现在,让我们深入了解一下所显示信息的存储方式。

注意存储在_id 中的数值。在我们这个例子中,_id是一个复合值。它是一个将customerId和以秒(epoch时间)表示的第一次交易时间串接起来的字符串。这样做的原因如下。

某个唯一客户的股票交易历史信息通过我们设计的网页显示出来。创建一个以customerId开始的复合值可以将history数组域中的所有对象有效地“组合”起来。使用一个正则表达式,我们就能迅速找到第一个完整的结果集:

我们将返回一个单独的文档。它包含了一个history数组,而这个数组中有多条准备显示的股票交易数据!

现在,假设有两条以上的交易。让我们看看1,000条交易的例子。这种模式是如何工作的?

让我们回到这个想法上来:数据应根据显示需要进行存储,每个桶应该包括足够多的交易,从而生成一个完整的页面。如果将某次显示设计成每页显示20条交易,那么就存储50个文档,每个桶包含20条交易(1,000条交易 / 20= 50 个文档)。

如需显示第1页,就从服务器提取第一个桶。如需显示第2页,就使用.skip(1) 跳过第一个桶,并从服务器提取第二个桶。如需显示第3页,就从服务器提取第三个桶。如需跳到第40页,就从服务器提取第40个桶。就是如此简单!

完成了,对吧?还没有,至少不完全如此。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mongoing中文社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档