为什么PostgreSQL查询在第一次新连接后的第一次请求中比在后续请求中慢?
使用几种不同的技术连接到postgresql数据库。第一次请求可能需要1.5秒。完全相同的查询将第二次花费.03秒。打开我的应用程序的第二个实例(连接到同一个数据库),第一个请求需要1.5秒,第二个请求需要.03秒。
由于我们使用的技术不同,它们在不同的点连接并使用不同的连接方法,因此我认为它与我编写的任何代码都没有任何关系。
我认为在第一个请求之前打开连接并不会执行“所有操作”,因此请求会产生一些开销。
因为我已经使用了数据库,并且保留了服务器所有内容都在内存中所以索引等应该不是问题。
编辑解释 - 告诉我有关查询的信息,老实说查询看起来很不错(索引等)。我真的认为postgresql在新连接的第一个查询上有某种开销。
我不知道如何证明/反驳这一点。如果我使用PG Admin III(pgAdmin版本1.12.3),所有查询看起来都很快。我第一次查询的任何其他工具都很慢。大部分时间它的速度并没有明显变慢,如果是这样的话,我总是将其用指数更新ram。但这显然不是那样的。如果我打开我的工具并执行任何其他返回结果的查询,则无论如何第二个查询都很快。如果第一个查询没有返回结果,那么第二个查询仍然很慢,那么第三个查询很快。
编辑2 即使我不认为查询与延迟有任何关系(每个第一个查询都很慢),这里有两个运行Explain(EXPLAIN ANALYZE)的结果
EXPLAIN ANALYZE
select * from company
where company_id = 39
输出:
"Seq Scan on company (cost=0.00..1.26 rows=1 width=54) (actual time=0.037..0.039 rows=1 loops=1)"
" Filter: (company_id = 39)"
"Total runtime: 0.085 ms"
和
EXPLAIN ANALYZE
select * from group_devices
where device_name ilike 'html5_demo'
and group_id in ( select group_id from manager_groups
where company_id in (select company_id from company where company_name ='TRUTHPT'))
输出:
"Nested Loop Semi Join (cost=1.26..45.12 rows=1 width=115) (actual time=1.947..2.457 rows=1 loops=1)"
" Join Filter: (group_devices.group_id = manager_groups.group_id)"
" -> Seq Scan on group_devices (cost=0.00..38.00 rows=1 width=115) (actual time=0.261..0.768 rows=1 loops=1)"
" Filter: ((device_name)::text ~~* 'html5_demo'::text)"
" -> Hash Semi Join (cost=1.26..7.09 rows=9 width=4) (actual time=0.297..1.596 rows=46 loops=1)"
" Hash Cond: (manager_groups.company_id = company.company_id)"
" -> Seq Scan on manager_groups (cost=0.00..5.53 rows=509 width=8) (actual time=0.003..0.676 rows=469 loops=1)"
" -> Hash (cost=1.26..1.26 rows=1 width=4) (actual time=0.035..0.035 rows=1 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 1kB"
" -> Seq Scan on company (cost=0.00..1.26 rows=1 width=4) (actual time=0.025..0.027 rows=1 loops=1)"
" Filter: ((company_name)::text = 'TRUTHPT'::text)"
"Total runtime: 2.566 ms"
答案 0 :(得分:1)
这是一个非常老的问题,但希望这可能会有所帮助。
第一个查询
似乎没有在使用索引,优化器正在对表进行顺序扫描。 在顺序扫描表时,如果数据适合缓冲区,那么优化器可以将整个表缓存在RAM中。有关更多信息,请参见this article。 为什么我不知道每个连接都在进行缓冲。无论如何,对于这种查询来说,顺序扫描是不理想的,并且可以通过正确的索引编制和统计信息来避免。
检查company
表的结构。确保company_id
是UNIQUE INDEX
或PRIMARY KEY
的一部分。
确保运行ANALYZE
,以便优化程序具有正确的统计信息。这将有助于确保在您的查询中使用company
的索引,而不是对表进行顺序扫描。
第二个查询
尝试使用INNER JOIN
来避免优化程序选择Hash Semi Join
,以获得更一致的性能和更简单的EXPLAIN PLAN
:
select gd.*
from group_devices gd
inner join manager_groups mg on mg.group_id = gd.group_id
inner join company c on c.company_id = mg.company_id
where gd.device_name like 'html5_demo%'
and c.company_name = 'TRUTHPT';
答案 1 :(得分:1)
我观察到相同的行为。如果启动新连接并多次运行查询,则第一次执行的速度比后续执行的速度慢25%。 (此查询已在其他连接中更早地运行,并且我已验证不涉及磁盘I / O。)我在第一次执行查询过程中使用perf对进程进行了分析,这就是我发现的:
如您所见,处理页面错误花费了大量时间。如果我分析第二次执行,则没有页面错误。 AFAICT,这就是所谓的次要/软页面错误。当进程首次访问共享内存中的页面时,会发生这种情况。那时,进程需要将页面映射到其虚拟地址空间(请参见https://en.wikipedia.org/wiki/Page_fault)。如果需要从磁盘读取页面,则称为主要/软页面错误。
此说明也符合我的其他观察结果:如果以后在同一连接中运行其他查询,则第一次执行的开销似乎取决于与访问的数据有多少重叠。第一个查询。
答案 2 :(得分:-1)
第一个请求将从磁盘读取块到缓冲区。 第二个请求将从缓冲区读取。
无论建立多少连接,结果都取决于该查询是否已被解析。
请注意,更改文字会重新分析查询 另请注意,如果查询尚未执行一段时间,则可能仍会发生物理读取,具体取决于许多变量。