PostgreSQL查询在函数中比它本身慢得多

00jrzges  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(1)|浏览(361)

我有一个PostgreSQL查询,它从一些大约16,000,000条记录的表中选择了多达10,000行。这大约需要3秒。问题是,如果我把完全相同的查询放入一个函数中,它会慢得多,大约需要1分20分钟。

设置概述

(Note实际上,设置稍微复杂一点,但整个问题可以用这种简化的方式来表达。)
我有一个关系型PostgreSQL数据库,其中包含数值网格的单元格和节点。一个网格由许多单元格组成,这些单元格又由一个或多个节点组成,节点基本上是空间中的一个点。因此,有三个表与我的问题相关:GridCellsGridNodesGridCellNodeLinks。表如下所示:

CREATE TABLE public."GridNodes" (
    "id" serial NOT NULL,
    PRIMARY KEY ("id")
);
CREATE TABLE public."GridCells" (
    "id" serial NOT NULL,
    "grid" integer NOT NULL,
    PRIMARY KEY ("id")
);
CREATE TABLE public."GridCellNodeLinks" (
    "gridCellId" int NOT NULL,
    "gridNodeId" int NOT NULL,
    FOREIGN KEY ("gridCellId") REFERENCES public."GridCells"("id"),
    FOREIGN KEY ("gridNodeId") REFERENCES public."GridNodes"("id")
);

问题:查询链表

我想使用分页来选择属于给定网格的所有节点,每页10,000个节点。为了获得更高的性能,我使用WHERE id >= firstIdOnPage而不是OFFSET pageNumber * 10000
对于网格1(有1,038,240个节点),查询如下所示:

SELECT
    N."id"
FROM public."GridCells" G
LEFT JOIN public."GridCellNodeLinks" L
    ON G."id" = L."gridCellId"
LEFT JOIN public."GridNodes" N
    ON N."id" = L."gridNodeId"
WHERE "grid" = 1
and N."id" >= 1030001
ORDER BY "id" ASC
LIMIT 10000;

如上所述,无论设置如何,这都需要几秒钟才能返回。然而,我将查询放入一个函数中:
(EDIT:实际上,我想将WHERELIMIT部分中的参数传递给这个函数,而不是硬编码它们。)

CREATE OR REPLACE FUNCTION GetNodes()
RETURNS TABLE(
    "id" INT
)
AS
    $BODY$
    BEGIN
        RETURN QUERY 
            SELECT
                N."id"
            FROM public."GridCells" G
            LEFT JOIN public."GridCellNodeLinks" L
                ON G."id" = L."gridCellId"
            LEFT JOIN public."GridNodes" N
                ON N."id" = L."gridNodeId"
            WHERE "grid" = 1
            and N."id" >= 1030001
            ORDER BY "id" ASC
            LIMIT 10000;
    END;
    $BODY$
LANGUAGE plpgsql
PARALLEL SAFE;

调用该函数SELECT * FROM GetNodes();需要将近30倍的时间,但只有当它找到的节点数小于LIMIT时。如果我为页面使用较小的第一个ID,例如N."id" >= 1020001,或者如果我缩短页面,例如LIMIT 8240,它仍然在几秒钟内完成。同样,这只发生在函数中,而不是在查询中。
你知道为什么这个函数的行为与“pure”查询有如此大的不同吗?

我所尝试的

  • 查询只做索引扫描,所以应该没问题。我试着在函数上做EXPLAIN ANALYZE,但正如here所解释的那样,它只给出了一个非决定性的“函数扫描”。使用auto_explain是没有选择的,因为我不是超级用户。
  • 有一些方法可以使查询更高效(例如使用子查询),并使其在500毫秒内完成。但这对函数没有影响。所以我想说这超出了这个问题的范围。
  • 我添加了PARALLEL SAFE,但根本没有效果。
  • (编辑)更改为language sql也没有效果。
e4yzc0pl

e4yzc0pl1#

原来这个问题与*DBeaver*只从查询结果中获取有限数量的行有关(默认为200行)。当查询在函数内时,必须先运行整个函数,然后才能获取200行。否则,DBeaver会秘密地将查询限制为200行,执行计划器可以考虑到这一点,使得查询更快。
该设置位于
属性〉编辑器〉数据编辑器
下,名为ResultSet获取大小
将其更改为10000会使我的查询和函数一样慢。因此,函数实际上很好地表示了查询的实际执行情况。我必须回头看看如何提高性能,但这是另一个问题。

相关问题