【问题标题】:Using "Cursors" for paging in PostgreSQL [duplicate]在 PostgreSQL 中使用“游标”进行分页 [重复]
【发布时间】:2012-10-20 00:27:41
【问题描述】:

可能重复:
How to provide an API client with 1,000,000 database results?

想知道Cursors的使用是使用PostgreSQL实现“分页”的好方法。

用例是我们希望向 API 客户端提供超过 100,000 行。我们认为实现这一点的一个好方法是允许客户端批量(页面)请求信息。客户端一次可以请求 100 行。我们将返回 100 行以及一个游标,然后当客户端准备好时,他们可以使用我们发送给他们的游标请求接下来的 100 行。

但是,我对游标的工作方式以及应该如何以及何时使用游标有点模糊:

  • 游标是否要求数据库连接保持打开状态?
  • 游标是否在事务中运行,锁定资源直到它们“关闭”?
  • 还有其他我不知道的“陷阱”吗?
  • 还有其他更好的方法来处理这种情况吗?

非常感谢!

【问题讨论】:

  • 我们在谈论什么样的客户?特别是,HTTP 客户端很容易“消失”...
  • 是的,HTTP 客户端,所以它们可以并且将会消失。
  • 如果您必须发布多个问题,请交叉链接问题。
  • 不是完全重复的。我发布两个问题的原因是一个解决了“我应该如何将数据发送给客户”这个更普遍的问题?这个人在问“如果我进行分页,那该怎么办?”。结束这个问题的你们是出色的开发人员,回答了很多问题。不过,你们都没有太多的提问经验。才华横溢的开发者往往不熟悉新手的视角。如果你不问很多问题,然后大方地关闭格式良好且表达清晰的问题,那么你就是在自讨苦吃。
  • 请注意,与 REST API 相关的术语“游标”与数据库查询游标完全不同。我怀疑这个问题,而且很多答案都将两者混为一谈。

标签: postgresql pagination


【解决方案1】:

对于 HTTP 客户端,不要使用游标来实现分页。为了可扩展性,您不希望在请求之间占用服务器资源。

相反,在您的查询中使用 LIMIT 和 OFFSET;见LIMIT and OFFSET in the Pg docs

但请确保您的表上的索引将支持这种形式的高效查询。

设计一个 RESTful API,以便客户端可以调用“next_url”(也在响应中传递)来获取下一组行。

【讨论】:

  • 对于非常大的偏移量(如 500,000 或 1,000,000),OFFSET 查询不是非常低效吗?
  • 为什么允许客户端一次检索 100 行这样庞大的数据集?我想说,要么客户端一次检索很多行(最好是所有他们需要的行),或者非常小的行集用于在真实屏幕上进行分页——最终用户所在的地方'不太可能翻阅 10 多个屏幕来获取他们想要的记录。
  • 我们需要向客户提供所有的信息。我的印象是,通过这样的 HTTP 连接返回 1,000,000 行会有问题,因为 HTTP 连接必须保持打开一段时间?
  • 您是否有其他逻辑方式来对数据进行分区(而不是任意按行数)?
  • 行的宽度是多少? 1M 行是多少 Mb?您是否正在压缩返回给客户端的数据?您的客户有什么样的互联网连接?
【解决方案2】:

游标是在处理大型数据集的小型 Intranet 应用程序中进行分页的合理选择,但您需要准备好在超时后丢弃它们。用户喜欢闲逛、去吃午饭、去度假两周等,然后让他们的应用程序继续运行。如果它是基于网络的应用程序,甚至还有什么是“正在运行”以及如何判断用户是否还在附近的问题。

它们不适合具有大量客户端数量的大型应用程序以及几乎随机进出的客户端,例如基于 Web 的应用程序或 Web API。我不建议在您的应用程序中使用游标,除非您的客户端数量相当少且请求率非常高......在这种情况下发送小批量的行将非常低效,您应该考虑允许范围请求等。

游标有几个成本。如果游标不是WITH HOLD,则必须保持事务打开。打开的事务可能会阻止 autovacuum 正常工作,从而导致表膨胀和其他问题。如果游标被声明为WITH HOLD 并且事务未保持打开状态,则您必须支付实现和存储可能较大的结果集的成本——至少,我认为保持游标是这样工作的。另一种方法同样糟糕,保持事务隐式打开直到游标被销毁并防止行被清理。

此外,如果您使用游标,则无法将连接交回连接池。每个客户端需要一个连接。这意味着更多的后端资源仅用于维护会话状态,并为您可以使用基于游标的方法处理的客户端数量设置了一个非常真实的上限。

与具有限制和偏移的无状态连接池方法相比,管理有状态、基于游标的设置也存在复杂性和开销。您需要让您的应用程序在超时后使游标过期,否则您可能会在服务器上面临无限资源使用,并且您需要跟踪哪些连接具有哪些游标针对哪些结果集针对哪些用户......

一般来说,尽管效率很低,LIMITOFFSET 可能是更好的解决方案。 It can often be better to search the primary key rather than using OFFSET, though.

顺便说一句,您正在查看 PL/pgSQL 中的游标文档。您需要 normal SQL-level cursors 来完成这项工作。


游标是否要求数据库连接保持打开状态?

是的。

游标是否在事务中运行,锁定资源直到它们 “关闭”了吗?

是的,除非它们是 WITH HOLD,在这种情况下它们会消耗其他数据库资源。

还有其他我不知道的“陷阱”吗?

是的,正如上面应该解释的那样。

【讨论】:

  • 这个话题很有趣。到今天为止,Graphql 推荐the pagination with cursor,但我还是对实现它的性能感到困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
相关资源
最近更新 更多