对于任何深入了解后端的人来说,这是一个回归基础的问题。
问题很简单我有http api 例如:
v1/foo?filter=F1&offset=100&limit=10
带有以下查询参数
此 API 回调到具有以下列的独立表上的 PG 数据库
我假设上述 api 的最优化查询是(在标志 col 上也有索引)
select * from table
where Flags=${Filter}
offset ${offset}
limit ${limit}
order by PK
但是如果你从数据库引擎方面来看,这是有史以来最糟糕的查询,数据库引擎必须在整个表/索引上运行where子句(假设行数超过1000万),然后按PK对其进行排序,然后限制和偏移它..这是最低效的事情...它消除了在 API 上进行分页的原因,你不觉得吗?
我的意思是它必须扫描整个表/索引过滤所有内容的结果,然后只返回其中的一部分并对所有页面重复此操作(虽然我们认为我们已经实现了分页以减少资源的负载和压力)
那么分页的正确答案是什么样的呢? 首先我们问 PG 这个表分布在多少个块/OS 文件上,然后我们每个 api 请求取一个块,使用 where 过滤条件扫描它,然后返回结果? 它有一些副作用,例如空页面响应(其中块针对过滤条件返回零结果,即使可能有更多块需要扫描,其中可能有结果)它更像是一个迭代器接口,服务器决定页面大小和偏移量应该是多少在屈服之前。
这似乎是管理资源的最佳方式,并且真正符合分页的定义(粗略地类比扫描一本书), 但这是问题,我看到没有数据库支持这样的东西,为什么? 限制和偏移总是感觉像是经过深思熟虑后,结果被完全编译然后应用......
或者还有其他运算符/方法来实现这个简单的需求? 或者我在这里遗漏了什么?
更新:
人们在评论中谈论Keyset分页,这是削减偏移子句的好方法,但仍然无法对页面进行结束停止,这意味着服务器可以长时间工作直到满足条件..
例如:1....10000000 我可以说 offset 100 limit 20000 这意味着 >100 limit 20000 所以直到它找到 20000 行它将继续工作..
加上我的理解是 pg 首先创建整个结果集,然后应用限制,这意味着它仍然会扫描整个索引/表
这与仅扫描一个文件并返回结果不同,我认为这使得服务器只做有限的工作,因此更可预测?
有4类型的分页可供选择
优点和缺点都在 https://www.cybertec-postgresql.com/en/pagination-problem-total-result-count/ 有详细记录,所以不会再这样做了。
专业人士:
缺点:
如何: Step1:通过
从系统表中获取总页数 Select relpages,relfilenode
FROM
pg_class c
WHERE
relname = 'Your_table_name'
ORDER BY relfilenode
Step2:通过
进行有限搜索SELECT *
FROM Your_table_name
WHERE ctid BETWEEN '(PAGE_START,0)'::tid AND '(PAGE_END,65535)'::tid
AND 'Add any more condition required'
瞧!!
这个想法是利用 PG 如何管理 8KB 的块/页面中的文件数据,每个页面已经定义,这是内存映射的,我们总是可以计算我们的查询将占用多少资源,因为我们知道我们有多少页面要求它读取,这也带来了查询时间的可预测性,最后节省了我们在其他列上建立大量索引的时间,因为我们处于有限的表扫描中,因此我们可以将任何条件与其组合,并且不需要单独对列进行排序或排序-ing等
示例:
创建一个表“hugedata”
CREATE TABLE hugedata (
pk SERIAL not null,
description text,
flags varchar(5) not null
);
插入一些巨大的数据:
INSERT INTO hugedata (flags)
SELECT substring((d::TEXT || '_' || (random() * 1000)::TEXT),0,5)
FROM generate_series(0,1000000,1) AS d(d);
询问表格有多少页和文件
Select relpages,relfilenode
FROM pg_class c
WHERE relname = 'hugedata'
ORDER BY relfilenode
根据任何额外条件选择要扫描的页面数并开始分页:)
SELECT *
FROM hugedata
WHERE ctid BETWEEN '(0,0)'::tid AND '(1000,65535)'::tid
AND pk>5