sql - 一定数量的记录后,Postgres 服务器性能下降

标签 sql performance postgresql cursor

我正在使用游标从大型 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 提供了一次加载完整分区的最佳机会。

最佳答案

我对性能的猜测如下。

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

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

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

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

现在,您如何解决这个问题。最好的方法是将过滤逻辑放入它所属的数据库中。毕竟,向应用程序返回数亿行数据并不是对数据库的良好利用。这是我首先要看的地方。

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

您可以做的一件事是重新创建表,按索引列对数据进行排序,然后重新建立索引(即将该列变成聚簇索引)。这将改善 future 的情况,但这个过程需要一些时间。

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

其次,如果您不需要特定顺序的数据,则只需拉取不带顺序的记录。这应该用全表扫描代替索引扫描,从而消除页面抖动。

关于sql - 一定数量的记录后,Postgres 服务器性能下降,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11702930/

相关文章:

c# - 如何在每个目录或子目录中找到包含特定字符串的最新文件?

java - 通过 Hibernate 从 PostgreSQL 数据库随机选择

java - Android Oreo 动画渲染问题

sql - 使用proc sql将多个不同结构的表堆叠到一张表中

sql - 如何将100万随机行插入表数据库Oracle

sql - 有没有办法计算列中 NULL 之间的非空数量?

python - 如何在Python中加快像函数一样的卷积?

sql - 在 Sequelize 中使每个用户 ID 的字段唯一?

postgresql - 在 postgres 请求中添加内容类型?

mysql - 获取外键的计数和分组