我正在使用游标从大型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,并按顺序(而不是父表)对每个子表运行游标来解决这个问题。我也从光标中删除了顺序,因为这似乎有所帮助,即使顺序是针对有序索引。
我的猜测是,这为postgres提供了一次加载完整分区的最佳机会。
答案 0 :(得分:3)
我对性能的猜测如下。
您有“indexed_column”的索引。 。 。这只是猜测(基于名称)。 Postgres使用索引列进行排序。此外,该表是以增量方式创建的,因此表的第一百万行左右都在数据库中的一组连续页面上。
如果确实如此,则会发生以下情况。排序很乐意转到索引并找到它需要的记录。如果页面尚未存在,它会将页面加载到内存中。在几乎所有情况下 - 对于第一百万行左右 - 页面都在那里,结果很快就会返回。
之后,发生了一些不好的事情。索引指定一行,带有该行的页面可能不在内存中。因此,它必须获取页面,通常替换(刷新)缓存中已有的页面。也就是说,每个行引用基本上都需要磁盘I / O.
顺便说一下,这种情况可能发生在任何表上,即使是不以特定方式创建的表。但是,在填充缓存之前,获得一百万行是很多行,除非它们是有序的。
现在,您如何解决问题。最好的方法是将过滤逻辑放入它所属的数据库中。毕竟,将数亿行返回给应用程序并不能很好地利用数据库。这是我看的第一个地方。
你可以做一些激烈的事情,看看是否放弃索引并对订单进行实际排序更快。如果上面的描述是正确的,那么它会更快 - 需要注意的是你会在第一行等待很长时间。
您可以做的一件事是重新创建表,按索引列对数据进行排序,然后重新编制索引(即将列转换为聚簇索引)。这将改善未来的事情,但这个过程需要一些时间。
另外两件可能尝试的事情。假设您只需要表中的一列子列,请创建一个临时表,然后从那里提取数据。有4亿条记录,这需要一段时间,但是,如果所需字段相对于原始记录较小,则会提高性能。
其次,如果您不需要特定顺序的数据,那么只需在没有订单的情况下提取记录。这应该用全表扫描替换索引扫描,这样可以消除页面抖动。
答案 1 :(得分:0)
我是第二个戈登的观点,并将添加我自己的缓存未命中经验。基本上发生的事情是,在某个时刻,缓存中的数据最终到期,并且突然间您有大量的磁盘I / O.
我为客户进行了批量付款存储过程,对于小批量付款,它可以很好地工作,但对于大型批次,它会窒息。经过一些测试后,我们注意到以下几点:
当支付了几百张发票时,它会表现得非常好。如果为供应商支付200-500张发票,它将开始减速,并且为供应商支付1000张或更多发票,它似乎会挂起。我和一位看过代码的PostgreSQL大师交谈,并立即建议缓存未命中。我们在考虑到这一点时重写了代码,性能变得可以容忍。
我建议你做的是确切地从应用程序的角度确定需要检索的内容,并在该查询中尽可能多地编写查询。我不确切知道你在做什么,所以我不知道排序是否足够。但是我已经看到HAVING子句解决了像这样的性能问题所以不仅要查看排序而且要查看聚合以及初始查询是否以任何方式将读取与写入相结合(例如通过字段列表中的函数调用)。这总是一个性能杀手,并且会使缓存失误异常痛苦(这实际上是我上面提到的学习经历中的一个主要问题)。
一般来说,我对PostgreSQL的体验是,当你获得应用程序所需的信息时,你的性能总是最好的,而且很多设置的处理逻辑都会被推送到数据库中。