Postgres 在非常大的表上选择

Rah*_*rma 7 postgresql amazon-rds amazon-web-services query-performance

目前 Postgres 12.5 上有一个非常大的表,包含超过 22 亿行。表的总大小(包括索引)为 500 GB。我们需要执行一个查询,以便从数据集中找到一组有效行并对它们进行更新。该查询看起来像这样:

select id, col4 from table where col1=$1 and col2=$2 and col3='f' and col4>0 order by col5 limit 10
Run Code Online (Sandbox Code Playgroud)

为了服务此查询,表上有一个索引ON (col1, col2, col5),并且查询使用此索引。到目前为止,一切都很好。当缓冲区未命中时数据库需要执行大量磁盘查找时,就会出现问题。这会导致查询等待DataFileRead

到目前为止,我们使用的是 16 vCPU 和 128 GB 的机器,存储类型为 io1,预配置 IOPS 为 20000 进行托管(托管在 AWS RDS 上)。我们一开始配置的 IOPS 约为 3000,并不断增加它,希望通过积极的自动清理和数据本地化,它能够稳定在某个值。autovacuum 的配置方式是每隔几天在此表上运行一次。我们最近遇到一个问题,读取 IOPS 开始达到 20000,并且应用程序变得太慢。由于我们无法再在之前的机器上提供超过 20000 的 IOPS,因此我们升级到了一台更大的机器,其大小恰好是原来的两倍。

在较大的计算机上,我们观察到即使读取 IOPS 现在也已降至约 5000,并且该计算机现在在峰值时间消耗的总体 IOPS 约为 6000,并且查询时间精确地减半。我们假设,这肯定与shared_bufferpostgres 现在可用于将热引用行保留在缓存中的更高版本有关。

问题是我们现在使用的机器以大约 5% 的 CPU 负载运行,并且还有 184 GB 的 RAM 仍未使用。总而言之,这台机器的利用率严重不足。我们希望通过对参数进行任何更改来使用较小的机器,以便该查询可以在一些可容忍的延迟限制下运行。我们在之前的机器上尝试过多次内存调整,以充分利用RAM。但增加到shared_buffer超过 40% 的 RAM 总是会导致查询变得非常慢,我们总是不得不将其恢复到之前的值。

共享一些 Postgres 数据库参数(当前在较大的机器上):

effective_cache_size: 130GB
shared_buffers: 66GB
work_mem: 4MB
maintenance_work_mem: 8.5GB
Run Code Online (Sandbox Code Playgroud)

PS:数据增长约为每天 3000 万条,所以情况会变得更糟。该数据库于 3 个月前投入生产使用。我们还需要有关构建可持续解决方案的建议。由于应用程序的性质,我们无法对表进行分区,除非它已经存在 6 个月或更久了。分片将是我们的最后手段,但我们希望在转向此解决方案之前用尽所有选择。

编辑:附加查询计划(第一个是当缓冲区中没有数据时,第二个是立即的后续查询命中)。性能看起来完全可以接受,因为我们使用的是一台较大的机器,但在较小的机器上花费了超过 1 秒的时间。

在此输入图像描述

在此输入图像描述

不,我们不需要一次更新十亿行。我们只需要更新一些。很难确定更新期间受影响的行数,但对于特定事务来说不会超过 20 行。我可以告诉你我们在这里想要实现的目标。它就像一个有容量的袋子,我们试图用仅接受有效条目(is和> 0)的值C来填充它。我们一次从数据库中查看 10 个条目,如果需要,可能会在数据库上进行后续相同的查询以获取下一个有效条目。在此过程中,仅更新,可以将其设置为零,因为它已被消耗,也可以设置为低于当前值的数字。col4col3'f'col4col4

寻找任何想法或建议。提前致谢。

jja*_*nes 4

您当前正在使用 col3 和 col4 上的过滤器删除 855 行,以便找到通过该过滤器的 10 行。因此,正如我所担心的那样,未能通过过滤器的事物可能比其他事物更罕见,但它们确实是障碍。下次你需要另外 10 件东西时,它们仍然会妨碍你。还有下一次。每次执行时,您不仅需要完成比所需工作量多 85 倍的工作,而且还要访问约 85 倍的页面。如果 col1 和 col2 的所有其他组合都会发生同样的情况,那么难怪您会不断耗尽缓存空间和 IOPS。当然,它没有理由就此停止,如果你没有什么可以摆脱它们的话,你可能会积累超过 850 个。

您可以使用部分索引来避免每次都访问这些行:

create index on t (col1, col2, col5) where col3='f' and col4>0;
Run Code Online (Sandbox Code Playgroud)

或者,每次 col3 变为 true 或 col4 变为 0 时,您可以删除该行,并且(可能)将其插入到某个历史表中(如果您需要保留某些记录)。