我正在使用PostgreSQL,我打算分页。目标表包含1M +行。原则上,这是直截了当的
SELECT * FROM myTable ORDER BY orderCol LIMIT <pageSize> OFFSET <offset>;
现在,当orderCol被索引时这很快,但是当orderCol没有索引时,这个数字会慢一个数量级。显然,在最坏的情况下,dbms被迫执行全表扫描,并且必须为每个请求的页面排序数据。
[编辑:更具体地说,orderCol
可能会发生变化,即在运行时确定。{/ p>
[ Edit2:索引orderCol
提高排序性能的一般假设似乎是错误的。如果我向orderCol
添加索引,查询时间会增加约70%。]
一个明显的解决方案是根据需要使用适当的索引创建临时表,并使用适当的数据填充表(...我认为)。但这复制了所有数据。
有没有办法可以“保留”请求之间的排序顺序?或者创建一个临时索引?
非常感谢你提前给出答案。
答案 0 :(得分: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
相比,它将查询时间减少了大约一个数量级。
答案 1 :(得分:1)
你遇到了几个问题:
您可能真的想索引所有可排序的列配置,至少是那些由您的应用程序经常排序的列配置。有关该主题的一些有趣见解in this blog。
即使您有索引,跳转到高页码也很慢,因为您必须遍历整个索引才能进行OFFSET
计数。试着看看你是否可以使用"seek method"。
搜索方法实际上跳转到上一页的最后一条记录后的第一条记录,例如
SELECT *
FROM myTable
WHERE orderCol > :lastValueforOrderCol
ORDER BY orderCol
LIMIT <pageSize>;
现在您不再按偏移量访问记录,但通过使用谓词,索引所有符合条件的orderCols
是必不可少的。
请注意,此方法不允许您跳转到固定的序号位置,例如OFFSET
。它的行为更像Twitter的“后续推文”的延迟加载。这可能是也可能不是。
注意,“搜索方法”也称为keyset paging。
由于您没有任何谓词,执行哑全表扫描并在内存中执行排序可能确实更快,而不是加载所有索引b树节点(可能分散在磁盘上)以跳过行。一旦添加选择性谓词,这种观察可能会被逆转。
我很惊讶,PostgreSQL的优化器不会自动选择全表扫描。
答案 2 :(得分:0)
什么阻止你只是索引这个列?
我有一个类似的问题,但对于一个20GB / 40M +行表,有很多“where”条件。数据是静态的,所以我让DW Server运行一个每日脚本,它只提取相关数据并创建了一个150k表。
更新
编辑:更具体地说,orderCol可能会改变,即在运行时确定
你的意思是,每当有人运行查询时,order列中的值都会改变(或者列可以是不同的,column1,colume2,......)?
查看物化视图。 http://wiki.postgresql.org/wiki/Materialized_Views
您可以在此查询上创建一个视图,然后从该视图中运行所有查询(并通过脚本每隔x min /小时/天删除它们)。比临时表更容易处理。
除此之外,还有一些技巧取决于详细的用例,但没有开箱即用的解决方案