8

我正在通过 HTTP 制作一个 API,它通过分页从 PostgreSQL 中获取许多行。在一般情况下,我通常通过 naive OFFET/LIMIT子句来实现这样的分页。但是,在这种情况下有一些特殊要求:

  • 有很多行,所以我相信用户无法到达终点(想象一下 Twitter 时间线)。
  • 页面不必是随机访问的,而只是顺序访问。
  • API 将返回一个 URL,其中包含指向连续块页面的光标标记。
  • 光标令牌不必永久存在,而是存在一段时间。
  • 它的排序经常波动(如 Reddit 排名),但连续游标应保持其一致的排序。

我怎样才能完成使命?我准备为它更改我的整个数据库架构!

4

2 回答 2

6

假设只是结果的顺序波动而不是行中的数据,弗雷德里克的回答是有道理的。但是,我建议添加以下内容:

  • 使用数组类型而不是内存将 id 列表存储在 postgresql 表中。在内存中执行此操作,除非您小心使用具有自动到期和内存限制的 redis 之类的东西,否则会为 DOS 内存消耗攻击做好准备。我想它看起来像这样:

    create table foo_paging_cursor (
      cursor_token ..., -- probably a uuid is best or timestamp (see below)
      result_ids integer[], -- or text[] if you have non-integer ids
      expiry_time TIMESTAMP
    );
    
  • 您需要确定 cursor_token 和 result_ids 是否可以在用户之间共享,以减少您的存储需求和每个用户运行初始查询所需的时间。如果它们可以共享,请选择一个缓存窗口,例如 1 或 5 分钟,然后根据新请求为该时间段创建 cache_token,然后检查是否已经为该令牌计算了结果 id。如果不是,请为该令牌添加一个新行。您可能应该在检查/插入代码周围添加一个锁来处理对新令牌的并发请求。

  • 安排一个清除旧令牌/结果的后台作业,并确保您的客户端代码可以处理与过期/无效令牌相关的任何错误。

甚至不要考虑为此使用真正的数据库游标。

将结果 id 保存在 Redis 列表中是另一种处理此问题的方法(请参阅LRANGE命令),但如果您沿着这条路走,请注意到期和内存使用情况。您的 Redis 键将是 cursor_token 并且 id 将是列表的成员。

于 2011-10-19T21:44:59.007 回答
1

我对 PostgreSQL 完全一无所知,但我是一个相当不错的 SQL Server 开发人员,所以我还是想试一试 :)

您希望用户在每个会话中最多浏览多少行/页面?例如,如果您希望用户为每个会话最多翻页 10 个页面 [每个页面包含 50 行],您可以设置该最大值,并设置 web 服务,以便当用户请求第一页时,您缓存10*50 行(或只是行的 ID:s,取决于您获得多少内存/同时用户数)。

这肯定会以多种方式帮助加速您的网络服务。而且很容易实现。所以:

  • 当用户从第 1 页请求数据时。运行一个查询(完成 order by、join 检查等),将所有 id:s 存储到一个数组中(但最多 500 个 id)。返回对应于 id:s 数组中位置 0-9 的数据行。
  • 当用户请求页面#2-10。返回对应于 id:s 在位置 (page-1)*50 - (page)*50-1 的数组中的数据行。

您也可以增加数字,500 个 int:s 的数组只会占用 2K 的内存,但这也取决于您希望初始查询/响应的速度。

我在一个实时网站上使用了类似的技术,当用户继续浏览第 10 页时,我只是切换到查询。我想另一种解决方案是继续扩展/填充数组。(再次运行查询,但不包括已包含的 id:s)。

无论如何,希望这会有所帮助!

于 2011-10-15T04:47:50.470 回答