3

我正在使用游标从大型 postgres 表中检索记录。(4亿条记录,使用子表对数据进行分区。)我的游标定义为:

select * from parent_table order by indexed_column

同时使用 JDBC 和 psql,前几十万次检索的性能是一致的。之后,它从悬崖上掉下来,再也没有恢复过来。在服务器上 CPU、内存和磁盘活动相当均匀;即,没有任何基于系统的东西是明显的罪魁祸首。我最初怀疑这可能是网络问题,但我已经从不同的网络复制了这个问题。

这是 psql:

db@dbdev> fetch 100000 from all_persons;
Time: 13995.910 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13852.955 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14037.631 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13818.516 ms
db@dbdev> fetch 100000 from all_persons;
Time: 13952.260 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14257.836 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14115.941 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14375.485 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14898.741 ms
db@dbdev> fetch 100000 from all_persons;
Time: 14086.004 ms
db@dbdev> fetch 100000 from all_persons;
Time: 59841.556 ms
db@dbdev> fetch 100000 from all_persons;
Time: 198176.211 ms
db@dbdev> fetch 100000 from all_persons;
Time: 162593.582 ms

这是 JDBC(一次检索 10000 个;左边的数字是插入回的已过滤记录集的计数):

...
536040 retrieve in 405; filtering in 28; insert in 1734
544739 retrieve in 413; filtering in 27; insert in 1713
553574 retrieve in 382; filtering in 27; insert in 1761
563167 retrieve in 348; filtering in 28; insert in 2019
572723 retrieve in 363; filtering in 27; insert in 2048
581736 retrieve in 363; filtering in 28; insert in 1784
591131 retrieve in 480; filtering in 28; insert in 1869
600260 retrieve in 377; filtering in 27; insert in 1831
608234 retrieve in 24074; filtering in 27; insert in 1566
616212 retrieve in 23711; filtering in 27; insert in 1649
624449 retrieve in 25913; filtering in 27; insert in 1587
632528 retrieve in 29981; filtering in 27; insert in 1527
641334 retrieve in 23231; filtering in 27; insert in 1728
650427 retrieve in 27883; filtering in 27; insert in 1996
659516 retrieve in 34422; filtering in 27; insert in 1774

虽然 psql 性能似乎越来越差,但 JDBC 性能至少在一百万条记录中保持大致一致(在大约 34k 和 17k 毫秒之间波动)。

性能突然下降的原因是什么?

(编辑)工作解决方案:

我通过将批处理大小(检索/插入)降低到 5000 并按顺序对每个子表(而不是父表)运行游标来解决这个问题。我还从光标中删除了 order by,因为这似乎有帮助,即使 order by 是针对有序索引的。

我的猜测是,这给了 postgres 一次加载完整分区的最佳机会。

4

2 回答 2

3

我对性能的猜测如下。

您在 "indexed_column" 上有一个索引。. . 这只是猜测(基于名称)。Postgres 使用索引列进行排序。此外,该表是增量创建的,因此该表的前一百万行左右都在数据库中的一组连续页面上。

如果这是真的,那么正在发生以下情况。排序很高兴进入索引并找到它需要的记录。如果页面不存在,它将页面加载到内存中。在几乎所有情况下——对于前一百万左右的行——页面都在那里,并且结果很快就会返回。

然而,在那之后,发生了一些不好的事情。索引指定了一行,而该行所在的页面可能不在内存中。因此,它必须去获取页面,经常替换(刷新)缓存中已经存在的页面。也就是说,每个行引用基本上都需要一个磁盘 I/O。

顺便说一句,这种情况可能发生在任何表上,即使是不是以特定方式创建的表。但是,获得一百万行是在填充缓存之前要取出很多行,除非它们是有序的。

现在,你怎么能解决这个问题。最好的办法就是把过滤逻辑放到数据库里,放在它所属的地方。毕竟,将数亿行返回给应用程序并不是对数据库的良好使用。那是我要看的第一个地方。

您可以做一些激烈的事情,看看是否删除索引并为 order by 进行实际排序是否更快。如果上面的描述是正确的,那么它会更快——但需要注意的是你会为第一行等待很长时间。

您可以做的一件事是重新创建表,按索引列对数据进行排序,然后重新编制索引(即将列变为聚集索引)。这将在未来有所改善,但这个过程需要一些时间。

另外两个可能的尝试。假设您只需要表中的一部分列,创建一个临时表,然后从那里提取数据。对于 4 亿条记录,这将需要一段时间,但是,如果所需的字段相对于原始记录而言较小,则会提高性能。

其次,如果您不需要按特定顺序排列的数据,那么只需按顺序提取记录即可。这应该将索引扫描替换为全表扫描,从而消除页面抖动。

于 2012-07-28T17:07:39.833 回答
0

我支持 Gordon 的观点,并将添加我自己的缓存未命中经验。基本上发生的情况是,缓存中的数据最终会在某个时间点到期,并且突然之间会有大量的磁盘 I/O。

我为客户做了一个批量支付存储过程,对于小批量支付,它运行良好,但对于大批量支付,它会窒息。经过一些测试,我们注意到以下几点:

当支付多达几百张发票时,它的表现会非常好。为供应商支付 200-500 张发票时,它会开始放缓,而为供应商支付 1000 或更多发票时,它似乎会挂起。我与一位 PostgreSQL 专家交谈,他查看了代码并立即建议缓存未命中。考虑到这一点,我们重写了代码,性能变得可以接受。

在您的情况下,我建议您准确地从应用程序的角度确定需要检索的内容,并编写查询以在该查询中尽可能多地执行。我不知道你在做什么,所以我不知道排序是否足够。但是,我已经看到 HAVING 子句解决了此类性能问题,因此值得关注的不仅是排序,还有聚合,以及初始查询是否以任何方式结合了读取和写入(例如通过字段列表中的函数调用)。这始终是性能杀手,并且会使缓存未命中异常痛苦(这实际上是我上面提到的学习经验中的一个主要问题)。

一般来说,我对 PostgreSQL 的经验是,当您准确地获得应用程序所需的信息时,您的性能总是最好的,并且尽可能多的设置处理逻辑被推送到数据库中。

于 2012-08-15T02:47:46.223 回答