1

我正在使用 PostgreSQL,我打算分页。目标表包含 1M+ 行。原则上,这是直截了当的

SELECT * FROM myTable ORDER BY orderCol LIMIT <pageSize> OFFSET <offset>;

现在,当 orderCol 被索引时,这很快,但当 orderCol 没有索引时,速度要慢一个数量级。显然,在最坏的情况下,dbms 被迫执行全表扫描,并且必须对请求的每个页面的数据进行排序。

[编辑:更具体地说,orderCol可能会改变,即在运行时确定。]

[ Edit2:索引提高排序性能的一般假设orderCol似乎是错误的。如果我向 中添加索引orderCol,查询时间会增加约 70%。]

一个明显的解决方案是根据需要创建一个带有适当索引的临时表,并用适当的数据填充表(……我认为)。但这会复制所有数据。

有没有一种方法可以“保留”请求之间的排序顺序?或者创建一个临时索引?

非常感谢您提前回答。

4

3 回答 3

1

好的,这是我想出的一个解决方案。

问题实际上是确定性行寻址和关系模型不兼容。我基本上想做的是告诉数据库下一步该往哪里看。但是由于请求是相互独立的,并且我们不能对表的物理结构做出任何假设,因此寻址行的唯一方法是使用唯一的列值。

因此,以下解决方案:

CREATE TEMPORARY TABLE orderTable( id int, rank int );
CREATE INDEX orderIdx ON orderTable( rank );
INSERT INTO orderTable (
  select id, row_number() over (order by orderCol) as rank 
  from myTable ORDER BY orderCol
);

现在,我可以按如下方式获取页面:

SELECT myTable.id, orderCol 
FROM myTable JOIN orderTable ON myTable.id=orderTable.id 
WHERE rank >= <lower> AND rank <= <upper>;

乍一看这听起来很疯狂,但是对于大约 128 的页面大小,myTable与在orderCol.

于 2013-06-06T13:31:42.607 回答
1

您遇到了几个问题:

是的,对未编入索引的列进行排序很慢

您可能真的想要索引所有可排序的列配置,至少是那些由您的应用程序经常排序的配置。在这个博客中有一些关于该主题的有趣见解。

偏移量很慢

即使您确实有索引,跳转到高页码也很慢,因为您必须遍历整个索引才能进行OFFSET计数。试试看能不能改用“seek 方法”

seek 方法本质上是跳转到上一页最后一条记录之后的第一条记录,例如

SELECT * 
FROM myTable 
WHERE orderCol > :lastValueforOrderCol
ORDER BY orderCol
LIMIT <pageSize>;

现在您不再通过偏移量访问记录,而是通过使用谓词,索引所有符合条件的记录orderCols是必不可少的。

请注意,此方法不允许您跳转到固定的序数位置,例如OFFSET. 它的行为更像 Twitter 对“后续推文”的延迟加载。这可能是可取的,也可能不是可取的。

注意,“seek 方法”也称为keyset paging

全表扫描可以比索引扫描更快

由于您没有任何谓词,因此执行哑全表扫描并在内存中执行排序确实可能更快,而不是加载所有索引 b-tree 节点(可能分散在磁盘上)以跳过行。一旦添加了选择性谓词,这种观察可能会被逆转。

不过,我很惊讶 PostgreSQL 的优化器不会自动选择全表扫描。

于 2013-10-27T09:13:46.880 回答
0

是什么阻止您仅索引此列?

我有一个类似的问题,但是对于一个 20GB/40M+ 行表有很多“where”条件。数据是静态的,所以我让 DW 服务器运行一个每日脚本,该脚本只是提取相关数据并创建了一个 150k 的表。

更新

编辑:更具体地说,orderCol 可能会改变,即在运行时确定

你的意思是每次有人运行查询时,订单列中的值都会改变(或者列可以不同,column1,colume2,...)?

研究物化视图。http://wiki.postgresql.org/wiki/Materialized_Views

您可以在此查询上创建一个视图,然后从该视图运行所有查询(并通过脚本每隔 x 分钟/小时/天删除它们)。比临时表更容易处理。

除此之外,还有一些技巧取决于详细的用例,但没有开箱即用的解决方案

于 2013-06-05T23:27:09.163 回答