实时性远大于“EXPLAIN ANALYZE”的执行时间(索引扫描)

rub*_*bik 3 postgresql performance query-performance

我想根据 ID 获取最多 100 行。id 是表的主键。

我编写的查询如下所示:

select * from table where id = any ($1);
Run Code Online (Sandbox Code Playgroud)

其中$1被插值为 ids 数组。

使用时EXPLAIN ANALYZE我得到以下计划(解释链接):

                                                                QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.43..44.98 rows=17 width=553) (actual time=100.048..834.209 rows=17 loops=1)
   ->  Index Scan using instagram_id_index_1000 on profiles_1000  (cost=0.43..44.98 rows=17 width=553) (actual time=100.046..834.163 rows=17 loops=1)
         Index Cond: (id = ANY ('{34491540,28977916,33241270,33609141,31043380,29364420,30247037,33311491,36267571,32886281,32366574,32569254,33038689,31089076,29416100,30455309,31570597}'::integer[]))
 Planning time: 424.512 ms
 Execution time: 834.280 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)

当我实际执行它时(使用\timing),我得到的结果在 2-5 秒范围内!我真的无法接受如此糟糕的表现。EXPLAIN ANALYZE首先提供的执行时间就已经很长了。

一些上下文:
1)数据库是本地的,所以没有网络延迟
2)我查询的表是物化视图
3)我也尝试了where id in (...)变体,但没有任何改变
4)我尝试以编程方式循环 ids 和为每个运行单独的查询,它会产生更好的结果(大约 1.5 秒)

这里有什么可以做的吗?我不敢相信 Postgres 在我的案例中的表现如此糟糕。服务器也有8个核心,是否可以尽可能并行化这个查询?

Erw*_*ter 6

首先显示的查询计划显示:

限制(成本=0.43..44.98行=17宽度=553)...

显然,您LIMIT向查询添加了一个子句,该子句不在您显示的查询中。如果您在第二次测试中没有执行相同的操作,您可能已经有了答案:带LIMIT(和不带ORDER BY)的相同查询可以在找到足够的行后立即停止,并且速度会快得多。也可能导致不同的查询计划开始。

原则

EXPLAIN ANALYZE测量Postgres 进程所花费的时间。测量是在服务器上进行的。与仅执行 SQL 命令相比,它增加了一些开销。对于非常便宜的命令,该开销可能超过查询成本。开销在某种程度上也取决于所使用的选项。您可以使用 最小化开销EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF)。(TIMING的选项EXPLAIN与 psql 的 没有任何关系\timing;用于切换每个计划节点的附加测量。)

手册:

ANALYZE选项使语句实际执行,而不仅仅是计划执行。然后,实际运行时间统计信息将添加到显示中,包括每个计划节点内花费的总运行时间(以毫秒为单位)以及实际返回的总行数。

关于该TIMING选项:

在输出中包括实际启动时间和每个节点花费的时间。在某些系统上,重复读取系统时钟的开销可能会显着减慢查询速度,因此FALSE在仅需要实际行计数而不是精确时间时将此参数设置可能很有用。始终测量整个语句的运行时间,即使使用此选项关闭节点级计时也是如此。ANALYZE仅当也启用时才能使用此参数。它默认为TRUE.

\timing另一方面,在 psql 中,测量发送命令和接收结果之间的时间 - 包括网络延迟。测量是在客户身上进行的。这可能是单独执行的很多倍。看来是你的情况。

由于上面提到的开销,在低延迟和通过线路发送的数据量最小的情况下可能会观察到相反的情况(特别是在同一服务器上执行 psql):\timing可以报告更快的时间。

还有第三个不错的选择:使用shell 命令time(显然也在客户端上进行测量)。看:

旁白:

当我实际执行它时(用\timing)...

您知道,它EXPLAIN ANALYZE确实执行了给定的查询,并具有所有副作用 - 而不仅仅是EXPLAIN.

我尝试以编程方式循环 ids 并为每个 ids 运行单独的查询,它会产生更好的结果(大约 1.5 秒)

循环可以根据值的统计信息生成针对每个实际输入值定制的查询计划。而大型IN或大型输入数组将根据一般估计生成计划。看:

您可以通过来自LATERAL(派生)表的联接或使用与(plpgsql)服务器端函数中的循环SELECT捆绑在一起的许多语句来实现与循环类似的效果 - 无需增加很多倍的网络延迟和/或规划UNION ALL高架。有关的: