Postgresql 为什么在一段时间后运行查询时,seq/index扫描会花费这么长时间?如何使它快速?

Postgresql 为什么在一段时间后运行查询时,seq/index扫描会花费这么长时间?如何使它快速?,postgresql,caching,indexing,postgresql-performance,Postgresql,Caching,Indexing,Postgresql Performance,问题: 我有一个连接三个表的查询。每当我在一段时间后(比如24小时)运行这个查询时,执行它都会花费很多时间。但从那时起,它将执行得非常快(大约快70倍)。我想知道第一次执行这么长时间的问题是什么,以及如何解决它 表格条件: 这些表是:property\u 2、property\u attribute\u 2和property\u address\u 2。每个分区都是一个较大表的分区(即属性,属性,以及属性)。另外,property\u attribute\u 2和property\u addre

问题:

我有一个连接三个表的查询。每当我在一段时间后(比如24小时)运行这个查询时,执行它都会花费很多时间。但从那时起,它将执行得非常快(大约快70倍)。我想知道第一次执行这么长时间的问题是什么,以及如何解决它

表格条件:

这些表是:
property\u 2
property\u attribute\u 2
property\u address\u 2
。每个分区都是一个较大表的分区(即
属性
属性
,以及
属性
)。另外,
property\u attribute\u 2
property\u address\u 2
中的行使用列
property\u id
具有指向
property\u 2
的引用键。这些列(
property.id
property\u attribute\u 2.property\u id
,以及
property\u address\u 2.property\u id
)都已编制索引

查询是:

select * from public.property_2 a 
inner join public.property_attribute_2 b on a.id = b.property_id 
left join public.property_address_2 c on a.id=c.property_id
在一段时间后运行查询计划时,它是:

Hash Right Join  (cost=670010.33..983391.75 rows=2477776 width=185) (actual time=804159.499..1065892.338 rows=2477924 loops=1)
  Hash Cond: (c.property_id = a.id)
  ->  Seq Scan on property_address_2 c  (cost=0.00..131660.48 rows=4257948 width=72) (actual time=289.781..247906.955 rows=4257973 loops=1)
  ->  Hash  (cost=595483.13..595483.13 rows=2477776 width=117) (actual time=803833.183..803833.185 rows=2477921 loops=1)
        Buckets: 32768  Batches: 128  Memory Usage: 3165kB
        ->  Hash Join  (cost=94193.96..595483.13 rows=2477776 width=117) (actual time=98061.326..802753.642 rows=2477921 loops=1)
              Hash Cond: (a.id = b.property_id)
              ->  Seq Scan on property_2 a  (cost=0.00..265463.84 rows=6176884 width=105) (actual time=1349.284..696922.438 rows=4272433 loops=1)
              ->  Hash  (cost=48702.76..48702.76 rows=2477776 width=20) (actual time=95497.307..95497.308 rows=2477921 loops=1)
                    Buckets: 65536  Batches: 64  Memory Usage: 2624kB
                    ->  Seq Scan on property_attribute_2 b  (cost=0.00..48702.76 rows=2477776 width=20) (actual time=464.476..94126.890 rows=2477921 loops=1)
Planning time: 4.034 ms
Execution time: 1065995.827 ms
第一次运行后的查询计划为:

Hash Right Join  (cost=670010.33..983391.75 rows=2477776 width=185) (actual time=8828.873..13764.283 rows=2477924 loops=1)
  Hash Cond: (c.property_id = a.id)
  ->  Seq Scan on property_address_2 c  (cost=0.00..131660.48 rows=4257948 width=72) (actual time=0.050..1411.877 rows=4257973 loops=1)
  ->  Hash  (cost=595483.13..595483.13 rows=2477776 width=117) (actual time=8826.620..8826.623 rows=2477921 loops=1)
        Buckets: 32768  Batches: 128  Memory Usage: 3165kB
        ->  Hash Join  (cost=94193.96..595483.13 rows=2477776 width=117) (actual time=1356.224..7925.850 rows=2477921 loops=1)
              Hash Cond: (a.id = b.property_id)
              ->  Seq Scan on property_2 a  (cost=0.00..265463.84 rows=6176884 width=105) (actual time=0.034..2652.013 rows=4272433 loops=1)
              ->  Hash  (cost=48702.76..48702.76 rows=2477776 width=20) (actual time=1354.828..1354.829 rows=2477921 loops=1)
                    Buckets: 65536  Batches: 64  Memory Usage: 2624kB
                    ->  Seq Scan on property_attribute_2 b  (cost=0.00..48702.76 rows=2477776 width=20) (actual time=0.023..630.081 rows=2477921 loops=1)
Planning time: 1.181 ms
Execution time: 13872.977 ms

还值得注意的是,我在这台机器上还有几个其他的Postgres数据库,不同的作业定期在这些数据库上使用不同的表。

区别一定是缓存:第一次,数据是从磁盘读取的,在后续的运行中,它们是在RAM中找到的。运行
EXPLAIN(analysis,BUFFERS)
track\u io\u timing=on
以确认这一点

然而,您的I/O系统似乎非常慢,或者您的表非常臃肿<代码>解释(分析,缓冲)将显示读取了多少块,这样您就知道了


如果膨胀确实是您的问题,
VACUUM(FULL)
会有所帮助。

如果冷缓存是您的问题,那么您可以在运行查询之前对其进行预热。Postgres附带了额外的模块,提供了一系列工具来填充缓存

如何在此处进行设置的说明:

然后你运行类似于:

SELECT pg_prewarm('public.property_2', 'prefetch');
SELECT pg_prewarm('public.property_attribute_2', 'prefetch');
SELECT pg_prewarm('public.property_address_2', 'prefetch');
当然,如果总是在不使用筛选谓词的情况下运行相同的
SELECT
query,那么最好只运行相同的查询来填充缓存,而不使用高级模块。可能是用cron作业计划的

。。。它们都被编入索引

正如您在
EXPLAIN
输出中看到的,您的索引没有使用。获取所有行时不使用筛选器谓词,因此索引通常不会有帮助。您可以使用
SELECT*
,即从所有联接的表中获取所有列,因此也将执行仅索引扫描。您可以通过在
SELECT
列表中只列出实际需要的列来提高性能

显然,更多的RAM(以及PostgreSQL缓冲区缓存的适当配置)也会有所帮助

或者,您可以使用
VACUUM
FULL
)或者使用具有适当列类型和顺序的优化表定义来减少RAM需求。不仅针对手头的表,还针对竞争相同资源的其他表(从而从缓存中逐出“您的”块)。见:


我已经猜到差异来自缓存。但我正在寻找一种解决方案,以更快地运行第一个查询。因为它将通过管道每月执行一次/两次。我可以做些什么来减少第一次执行的运行时间吗?e、 g.通过提前兑现数据。同时,缓冲区和表结构看起来很好。共享命中率随着每次执行而增加,I/O时间减少。而且这张桌子没有浮肿。非常感谢。是的,冷缓存是问题所在,它使用pg_预热解决了问题。有关详细信息,将根据所需的筛选器对表进行分区。例如,每个分区代表一个城市。因此不需要更多的过滤器。另外,我没有使用
select*
,这里我只是删除了列名,以便更具体地说明问题。