一起执行多个功能而不会降低性能

时间:2015-05-20 13:33:30

标签: postgresql plpgsql database-performance query-performance postgresql-performance

我有这个过程,必须使用pl / pgsql进行一系列查询:

--process:
SELECT function1();
SELECT function2();
SELECT function3();
SELECT function4();

为了能够在一次调用中执行所有操作,我创建了一个过程函数:

CREATE OR REPLACE FUNCTION process()
  RETURNS text AS
$BODY$
BEGIN
    PERFORM function1();
    PERFORM function2();
    PERFORM function3();
    PERFORM function4();
    RETURN 'process ended';
END;
$BODY$
  LANGUAGE plpgsql

问题是,当我总结每个函数自身的时间时,总计是200秒,而函数process()所花费的时间超过一个小时!

也许这是一个内存问题,但我不知道我应该更改postgresql.conf上的哪个配置。

数据库在PostgreSQL 9.4上运行,在Debian 8中运行。

2 个答案:

答案 0 :(得分:10)

您评论说4个功能必须连续 运行 。因此,可以安全地假设每个函数都使用上一个函数修改过的表中的数据。这是我的主要嫌疑人。

任何Postgres函数都在外部上下文的事务中运行。因此,如果打包到另一个函数中,所有函数共享相同的事务上下显然,每个人都可以看到对以前功能的数据的影响。 (即使效果对其他并发事务仍然是不可见的。)但统计数据不会立即更新。

查询计划基于所涉及对象的statistics。 PL / pgSQL在实际执行之前不会计划语句,这对您有利。 Per documentation:

  

在函数中首次执行每个表达式和SQL命令时,   PL / pgSQL解释器解析并分析命令来创建一个   准备好的语句,使用SPI管理器的SPI_prepare函数。

PL / pgSQL 可以 缓存查询计划,但只能在同一会话中 和(在第9.2页)只有在几次执行后才能显示相同的查询计划才能最佳地重复工作。如果您怀疑这对您来说是错误的,您可以使用动态SQL来解决它,每次都会强制执行一个新计划:

EXECUTE 'SELECT function1()';

但是,我看到的最有可能的候选人是导致查询计划较差的无效统计信息。函数内的SELECT / PERFORM语句(相同的)快速连续运行,autovacuum没有机会在一个函数和下一个函数之间启动和更新统计信息。如果一个函数基本上改变下一个函数正在使用的表中的数据,则下一个函数可能将其查询计划基于过时的信息。典型示例:具有几行的表格中填充了数千行,但下一个计划仍然认为“小”表的顺序扫描速度最快。你说:

  

当我总结每个函数单独使用的时间时,总数为   200秒,而函数process()所用的时间更长   超过一个小时!

究竟“本身”是什么意思?您是在一次交易或个别交易中运行它们吗?甚至可能介于两者之间?这将允许autovacuum更新统计信息(通常相当快),并可能根据更改的统计信息导致完全不同的查询计划。

您可以使用auto-explain检查 plpgsql函数中的查询计划

如果您可以识别此类问题,则可以在语句之间强制ANALYZE。只需要几个SELECT / PERFORM语句,您就可以使用更简单的 SQL函数并完全避免计划缓存(但请参见下文!):< / p>

CREATE OR REPLACE FUNCTION process()
  RETURNS text AS
$func$
   SELECT function1();

   ANALYZE some_substantially_affected_table;

   SELECT function2();
   SELECT function3();

   ANALYZE some_other_table;

   SELECT function4();
   SELECT 'process ended';  -- only last result is returned
$func$  LANGUAGE sql;

此外,只要我们没有看到被调用函数的实际代码,就可以有任意数量的其他隐藏效果
示例:您可以SET LOCAL ...一些配置参数来提高function1()的效果。如果在不影响其余事务的单独事务中调用。效果只持续到交易结束。但如果在一次交易中被调用,它也影响其余的......

基础:

Plus:事务累积锁定,这会锁定越来越多的资源,并可能导致并发进程的摩擦力增加。所有锁都在事务结束时释放。如果可能的话,最好在 单独的事务中运行大型函数 ,而不是包装在单个函数中(以及事务处理)。最后一项与@klinIMSoP已涵盖的内容相关。

答案 1 :(得分:3)

警告未来读者(2015-05-30)。

问题中描述的技术是有效阻止服务器的最明智的方法之一。

在某些公司中,使用此技术可以立即终止雇佣合同的形式获得奖励。

尝试改进此方法是没用的。它简单,美观,充分有效。

在RDMS中,交易支持非常昂贵。执行事务时,服务器必须创建并存储有关对数据库所做的所有更改的信息,以便在成功完成的情况下使这些更改在环境(其他并发进程)中可见,并且如果失败,则在事务之前恢复状态尽快地。因此,影响服务器性能的自然原则是在一个事务中包括最少数量的数据库操作,即。只有必要的。

Postgres功能在一个事务中执行。放置许多可以独立运行的操作严重违反了上述规则。

答案很简单:就是不要这样做。函数执行不仅仅是执行脚本。

在用于编写应用程序的过程语言中,还有许多其他可能通过使用函数或脚本来简化代码。还可以使用shell运行脚本。

如果有可能在函数中使用事务,那么使用Postgres函数就可以了。目前,这种可能性并不存在,尽管关于这个问题的讨论已有很长的历史(你可以在postgres mailing lists中阅读它。)