如何向 API 客户端提供 1,000,000 个数据库结果?

问题描述 投票:0回答:3

跟进我之前的问题:

在 PostgreSQL 中使用“游标”进行分页

为 API 客户端提供 1,000,000 个数据库结果的好方法是什么?

我们目前正在使用 PostgreSQL。一些建议的方法:

  • 使用游标进行分页
  • 使用随机数进行分页(在每个查询中添加“GREATER THAN ORDER BY”)
  • 使用 LIMIT 和 OFFSET 进行分页(对于非常大的数据集会崩溃)
  • 保存信息到文件,让客户端下载
  • 遍历结果,然后将数据发布到客户端服务器
  • 仅将密钥返回给客户端,然后让客户端从 Amazon S3 等云文件中请求对象(仍然可能需要分页才能获取文件名)。

有什么我没有想到的,它比这些选项中的任何一个都简单而简单,而且要好得多?

api postgresql pagination distributed-computing
3个回答
34
投票

该表有一个主键。好好利用它。

代替

LIMIT
OFFSET
,使用主键上的过滤器进行分页。您在评论中暗示了这一点:

使用随机数进行分页(给每一个添加“GREATER THAN ORDER BY” 查询 )

但是你应该怎么做并不是随机的。

SELECT * FROM big_table WHERE id > $1 ORDER BY id ASC LIMIT $2

允许客户端指定这两个参数,它看到的最后一个 ID 和要获取的记录数。您的 API 必须具有占位符、额外参数或替代调用以“获取 first n ID”,它从查询中省略了

WHERE
子句,但这是微不足道的。

这种方法将使用相当有效的索引扫描来按顺序获取记录,通常避免排序或需要遍历所有跳过的记录。客户端可以决定一次需要多少行。

此方法在一个关键方面不同于

LIMIT
OFFSET
方法:并发修改。如果您
INSERT
使用比某些客户已经看到的键 low 的键进入表,这种方法根本不会改变其结果,而
OFFSET
方法将重复一行。类似地,如果您
DELETE
一行的 ID 低于已经看到的 ID,则此方法的结果不会改变,而
OFFSET
将跳过一个看不见的行。不过,具有生成键的仅附加表没有区别。

如果您事先知道客户需要整个结果集,那么最有效的做法就是将整个结果集发送给他们,而无需任何分页业务。这就是我 would 使用游标的地方。从数据库中读取行,并以客户端接受它们的速度将它们发送给客户端。这个 API 需要限制客户端的速度,以避免后端负载过大;对于慢速客户端,我可能会切换到分页(如上所述)或将整个游标结果假脱机到一个临时文件并关闭数据库连接。

重要警告

  • 需要
    UNIQUE
    约束 /
    UNIQUE
    索引或
    PRIMARY KEY
    才能可靠
  • 限制/偏移的不同并发修改行为,见上文

1
投票

让 API 接受开始的偏移量和要返回的记录数。这是一种分页,客户端可以确定在一个页面请求中返回多少条记录。 API 还应该返回查询可能的记录总数,以便客户端知道有多少“页面”,或者当返回的记录数为零或小于要求的记录。您可以在 PostgresSQL 查询中通过在 SELECT 语句中使用 OFFSET 子句(从哪条记录开始检索)和 LIMIT 子句(要返回的记录数)来控制这一点。


0
投票

一般流程

  1. 确定搜索条件。

  2. 确定唯一排序键。 (它可以是主键,或包含主键。)

  3. 为搜索条件创建 B 树索引plus 排序键。

  4. 以排序键为游标进行查询。使用排序键的最后一个值作为下一个光标。


例子

搜索条件是

tenant_id
。排序键是
(created, id)
.

CREATE TABLE item (
  id bigserial PRIMARY KEY,
  tenant_id int REFERENCES tenant (id),
  created timestamptz NOT NULL
);

CREATE INDEX ON item (tenant_id, created DESC, id);
GET /<tenant_id>/items?cursor=<cursor_created>,<cursor_id> HTTP/1.1

然后进行查询:

SELECT *
FROM item
WHERE tenant_id = <tenant> AND (<cursor_created>, <cursor_id>) < (created, id)
ORDER BY created DESC, id DESC
LIMIT <page_size>

并根据最后的

created
id
结果返回游标值。

© www.soinside.com 2019 - 2024. All rights reserved.