【问题标题】:Smart pagination algorithm that works with local data cache与本地数据缓存一起使用的智能分页算法
【发布时间】:2017-02-07 01:51:27
【问题描述】:

这是我一直在思考的一个问题,但我还没有编写任何代码,因为我首先想解决一些我正在努力解决的一般问题。这是主要的。

背景

单页网络应用程序向某个远程 API(在我们的控制之下)发出数据请求。然后它将这些数据存储在本地缓存中并从那里提供页面。理想情况下,应用在离线时仍能保持完整功能,包括创建新对象的能力。

约束

  • 假设服务器端的产品数据库包含 +- 50000 个产品 (50Mb)
  • 假设没有 db 类型,我们通过 REST/GraphQL 接口与之交互
  • 假设单个产品记录
  • 假设结果集的最大有效负载为 256kB
  • 假设客户端最大存储空间为 5MB
  • 假设每次搜索的搜索结果集介于 0 ... 5000 个项目之间

挑战

挑战在于定义一种无状态但(网络)有效的方式从结果集中获取页面,以便确定我们将获得哪些结果。

示例

在传统分页中,当使用此 url 获取某些查询的下 100 个结果时:

https://example.com/products?category=shoes&firstResult=100&pageSize=100

搜索结果可能如下所示:

{
  "totalResults": 2458,
  "firstResult": 100,
  "pageSize": 100,
  "results": [
    {"some": "item"},
    {"some": "other item"},
    // 98 more ...
  ]
}

问题在于,根据这些信息,无法准确地获取特定页面上的对象。因为当我们请求下一页时,结果集可能已经改变(由于数据库的变化),影响了哪些项目是结果集的一部分。即使是很小的更改也会产生很大的影响:从数据库中删除的一个项目恰好位于结果集的第 0 页,这将改变我们在请求所有后续页面时将获得的结果。

目标

我正在寻找一种机制来使结果集的定义独立于未来的数据库更改,因此,如果有人在寻找鞋子并获得包含 2458 个项目的结果集,他实际上可以可靠地获取该结果集的所有页面即使它受到数据库后来更改的影响(我打算不真正删除项目,但为此目的在它们上设置一个已删除标志)

目前的想法

我看到了一个解决方案,其中结果集包含一个"pages" 属性,该属性是一个数组,其中包含该页面中项目的第一个和最后一个 id。假设您的 ID 数量不断增加,并且您并没有真正从数据库中删除项目,那么两个 ID 之间的项目数量是恒定的。这意味着应用程序可以获取这两个 ID 之间的所有项目,并始终获取完全相同的项目。此解决方案的问题在于,它仅在列表按 ID 顺序排序时才有效……我需要自定义排序选项。

我现在想出的唯一方法是只发送结果集中所有 ID 的列表...这样可以通过 SELECT * FROM products WHERE id IN (3,4,6,9,...) 获取页面...但这感觉相当不雅。 ..

我希望它不会太宽泛或太理论化。我有一个基于 Web 的数据库,只是不知道如何使用它进行分页。我正在寻找能够帮助我找到学习方向的答案,而不是完整的解决方案。

【问题讨论】:

  • 我可能遗漏了一些东西,但是如果用户能够离线创建新对象,则索引不能是连续的(除非您对离线对象有不同的 id)。
  • 通常人们不关心这些,只是获取项目,您的记录更新速度有多快,这会成为一个问题?
  • @fpg1503 系统将使用可以离线工作的分布式 ID 生成器。所以客户端在插入记录之前就知道记录的 ID。
  • 我正在寻找的是一种捕获结果集的方法,使其随着时间的推移保持稳定。个别项目的内容可能会改变,但我不希望项目的顺序或集合中的哪些项目发生变化。这样做的原因是我以“无限滚动列表”的形式呈现项目,尽最大努力保持您拥有包含所有项目的列表的错觉,即使实际上只有一小部分在同时屏幕。查看bridalapp.com/wedding-dresses 以查看示例。我希望它扩展到 10.000 多个项目。
  • @fpg1503 我想直到现在我才理解您关于索引不是顺序的评论。你的意思是它不能保证只上升对吗?我想你是对的。我目前正在使用这个手卷发电机:ws.suid。服务器将 ID 块分发给客户端,它们可以在离线时缓存和使用。它会随着时间的推移而增加,但由于缓存的原因,可以在使用较高 ID 后使用较低 ID。看来我的问题比我想象的要多......

标签: algorithm pagination


【解决方案1】:

版本控制数据库是结果集一致性的答案。 每条记录都有主 ID、修改计数器(版本号)和修改/创建的时间戳。无需修改记录 r,而是添加具有相同 id、版本号+1 和 sysdate 的新记录以进行修改。

在获取响应中添加 DB request_time(由于客户端/服务器之间可能存在时间差异,请勿使用客户端时间戳)。第一页正常提供,但您将 sysdate 作为 request_time 返回。其他页面的服务方式不同:您为每个版本化表添加诸如 modify_time

【讨论】:

  • 感谢您的回复!是的,我虽然关于这个。虽然它有点重。理想情况下,与“正常”数据模型相比,我希望进行最少的更改。更改 DELETE 操作以设置 removed 标志而不是实际删除的影响要小得多。但你是对的,这种方法可行。
  • 我决定接受这个,因为这是我认为的最佳选择。我仍然认为我会选择一个不那么严格的版本,但使用你的想法作为时间戳。
【解决方案2】:

您可以在查询第一次到达时将ID的结果集缓存在服务器端,并将唯一的ID返回给前端。此唯一 ID 对应于该查询的结果集。所以现在前端可以请求类似next_page 的东西,它具有它第一次进行查询时获得的唯一ID。您仍然应该继续将DELETE 操作更改为removed 操作的方法,因为它可以确保结果集中的所有条目都不会被删除。您可以在前端到达结果集末尾时将查询的结果集从缓存中丢弃,也可以对缓存条目的生命周期设置时间限制。

【讨论】:

  • 是的,我认为这行得通。唯一的缺点是它需要服务器跟踪“打开”查询。可以通过删除未使用 x 时间的查询状态的 cron 作业来完成。感谢您的意见!
猜你喜欢
  • 2018-05-07
  • 2010-09-14
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 2017-08-25
  • 2010-10-17
  • 2021-11-23
  • 2011-05-24
相关资源
最近更新 更多