PostgreSQL:如何在函数中并行运行查询?

Posted

技术标签:

【中文标题】PostgreSQL:如何在函数中并行运行查询?【英文标题】:PostgreSQL: How to run query in parallel in function? 【发布时间】:2019-09-24 12:02:52 【问题描述】:

我有一个简单的查询

select bod_vykonu_kod, count(1)
from cdc_s5_zdroj
group by 1
order by 1 desc;

按应有的方式并行运行。 解释分析:https://explain.depesz.com/s/auVt

然后,如果我将相同的查询放入函数中,它不会并行运行。我将它作为 STABLEVOLATILE 进行了尝试,但仍然没有平行。我还添加了 PARALLEL SAFE,但没有区别。

CREATE OR REPLACE FUNCTION 
test_par () 
returns table (
t_column1 bigint,
t_column2 bigint
)

volatile
PARALLEL SAFE
AS $dbvis$

BEGIN

RETURN QUERY
select bod_vykonu_kod, count(1)
from cdc_s5_zdroj
group by 1
order by 1 desc;

END;
$dbvis$ LANGUAGE plpgsql

解释分析易失性:https://explain.depesz.com/s/glFO 解释分析稳定:https://explain.depesz.com/s/vnXOExplain analyze stable and parallel safe:https://explain.depesz.com/s/QlKM

x86_64-pc-linux-gnu 上的 PostgreSQL 11.5,由 gcc (GCC) 4.8.5 编译 20150623(红帽 4.8.5-36),64 位 max_parallel_workers = 8 max_parallel_workers_per_gather = 4

是我做错了什么还是函数不支持这样的并行执行?

【问题讨论】:

【参考方案1】:

我在使用 plpgsql 函数时遇到了同样的问题。返回查询从未并行运行。我能找到的唯一解决方案是做这样的事情,因为CREATE TABLE AS 使用并行处理:

CREATE OR REPLACE FUNCTION 
test_par () 
returns table (
t_column1 bigint,
t_column2 bigint
)

volatile
AS $dbvis$

BEGIN
CREATE TEMPORARY TABLE my_temp ON COMMIT DROP AS
select bod_vykonu_kod, count(1)
from cdc_s5_zdroj
group by 1
order by 1 desc;

RETURN QUERY SELECT * FROM my_temp;
DROP TABLE IF EXISTS my_temp;
END;
$dbvis$ LANGUAGE plpgsql

这并不理想,但就我的情况而言,它仍然比不使用并行处理快很多。

更新:从姿势 14 开始,返回查询现在将使用并行工作器。我们不再需要使用 create table 解决方法。

【讨论】:

据我所知,这是因为可以暂停处理,请参阅the documentation。不过,我还没有验证这一点。 是的,我也是这么想的,尽管他们明确提到 FOR x IN query LOOP 而不是 RETURN QUERY 似乎很奇怪,这对我来说似乎更常见但不太明显。 @LaurenzAlbe 我不认为 RETURN QUERY 实际上可以暂停。与 FOR x IN QUERY 不同,它在返回之前收集所有结果。这闻起来像一个错误,或者至少是一个恶意功能,有人可能想查看邮件列表。 “RETURN NEXT 和 RETURN QUERY 的当前实现在从函数返回之前存储整个结果集,如上所述。” postgresql.org/docs/current/plpgsql-control-structures.html 谢谢它确实有效,但在我的情况下它不是正确的解决方案 不错的解决方法,在我的情况下似乎可以正常工作【参考方案2】:

我深入研究了代码,看看为什么RETURN QUERY 不支持并行执行。

原因是它使用游标以50个批次获取查询结果,并且使用游标执行的查询不是并行运行的(因为可能会暂停执行)。

这是来自src/pl/plpgsql/src/pl_exec.c的函数exec_stmt_return_query中的相关代码:

exec_stmt_return_query(PLpgSQL_execstate *estate,
                       PLpgSQL_stmt_return_query *stmt)

[...]
    if (stmt->query != NULL)
    
        /* static query */
        exec_run_select(estate, stmt->query, 0, &portal);
    
[...]
    while (true)
    
        uint64      i;

        SPI_cursor_fetch(portal, true, 50);

[...]

【讨论】:

感谢您的解释,尽管我必须说这种行为非常不幸,因为我使用函数来封装我们前端的查询。希望在未来的一些更新中会有一些解决方案。 向 pgsql-hackers 邮件列表写投诉。这是所有重要人物都会阅读的内容。

以上是关于PostgreSQL:如何在函数中并行运行查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何运行从 postgresql 中的函数生成的查询

了解 PostgreSQL 并行查询的会话中工作池中剩余多少并行工作人员?

PostgreSQL CTE 的一般并行性

PostgreSQL 是不是并行查询分区?

PostgreSQL 函数内部的智能逻辑查询性能

使用 SPI 的 PostgreSQL 并行查询可能吗?