在不损失性能的情况下同时执行多个功能

Execute multiple functions together without losing 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 中。

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

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

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

尝试改进此方法是无用的。简单、美观、足够有效。


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

一个 Postgres 函数在一个事务中执行。把许多可以独立运行的操作放在里面,严重违反了上述规则。

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

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

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

您评论说这 4 个功能必须运行连续。因此可以安全地假设每个函数都使用来自 tables 的数据,这些数据已被前一个函数修改。那是我的主要嫌疑人。

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

查询计划基于 statistics on involved objects. PL/pgSQL does not plan statements until they are actually executed, that would work in your favor. Per documentation:

As each expression and SQL command is first executed in the function, the PL/pgSQL interpreter parses and analyzes the command to create a prepared statement, using the SPI manager's SPI_prepare function.

PL/pgSQL 可以缓存查询计划,但只能在内session 并且(至少在 pg 9.2+ 中)只有在几次执行显示相同的查询计划重复运行效果最好之后。如果您怀疑这对您来说是错误的,您可以使用动态 SQL 解决它,它每次都会强制执行一个新计划:

EXECUTE 'SELECT function1()';

但是,我看到的最有可能的候选人是导致劣质查询计划的无效统计信息。函数内的 SELECT / PERFORM 语句(同样的事情)是 运行 快速连续的, autovacuum 没有机会在一个函数和函数之间启动和更新统计信息下一个。如果一个函数 实质上 更改了下一个函数正在使用的 table 中的数据,则下一个函数可能会根据过时的信息制定查询计划。典型示例:具有几行的 table 填充了数千行,但下一个计划仍然认为顺序扫描对于“小”table 是最快的。您声明:

when I sum the time that each function takes by itself, the total is 200 seconds, while the time that the function process() takes is more than one hour!

究竟“本身”是什么意思?您 运行 他们是在单笔交易中还是在单笔交易中?也许中间有一段时间?这将允许 autovacuum 更新统计信息(通常相当快)并可能导致基于更改后的统计信息的完全不同的查询计划。

您可以使用 auto-explain

检查查询计划 inside plpgsql 函数

如果您能识别出此类问题,则可以在语句之间强制执行 ANALYZE。就此而言,对于几个 SELECT / PERFORM 语句,您不妨使用更简单的 SQL 函数 并完全避免计划缓存(但见下文!):

CREATE OR REPLACE FUNCTION process()
  RETURNS text
  LANGUAGE sql 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$;

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

基础知识:

另外: 事务会累积锁,这会绑定越来越多的资源,并可能导致与并发进程的摩擦增加。所有锁都在事务结束时释放。如果可能的话,运行 中的大函数最好分开事务 ,而不是包含在单个函数(以及事务)中。最后一项与 and 已经涵盖的内容有关。