ash*_*ash 5 postgresql performance plpgsql functions postgresql-performance
这项任务可能看起来很深奥,但我还是想创建某种 POC。
我的目标是让 PostgreSQL 数据库(版本 10)向使用它的应用程序公开一个 API。
API 需要采用一组 UDF 的形式:所有函数都属于一个公共方案,这是应用程序唯一可以访问的。桌子和其他东西隐藏在私人计划中。几乎就像,您知道的,面向对象的数据库。
这就是我试图让它发挥作用的原因:
好吧,没关系。
因此,当我尝试创建一些非常简单的函数(例如从表中获取所有记录)时,我提到它们总是比它包装的查询慢。虽然这本身是完全可以接受和理解的,但时间差异可能很大。因此,无法接受。
我有一张这样的桌子。
CREATE TABLE notifications (
id SERIAL PRIMARY KEY,
source_type INTEGER NOT NULL,
content JSONB,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
)
Run Code Online (Sandbox Code Playgroud)
并且其中有 >120k 条记录。
想象一下,我们想要获得所有这些。
在这里,我们通过一个简单的查询来完成。没有索引,JSONB 数据几乎每条记录 1kb。
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM private.notifications;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on private.notifications (cost=0.00..16216.13 rows=120113 width=877) (actual time=0.015..496.473 rows=120113 loops=1)
Output: id, source_type, content, created
Buffers: shared hit=15015
Planning time: 0.063 ms
Execution time: 973.935 ms
Run Code Online (Sandbox Code Playgroud)
496 毫秒。
现在让我们尝试使用像这样的 pl/pgsql 函数:
CREATE OR REPLACE FUNCTION notifications_get()
RETURNS SETOF private.notifications AS
$$
BEGIN
RETURN QUERY SELECT * from private.notifications;
END
$$
LANGUAGE 'plpgsql'
SECURITY DEFINER;
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM notifications_get();
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Function Scan on notifications_get (cost=0.25..10.25 rows=1000 width=48) (actual time=99.561..589.129 rows=120113 loops=1)
Output: id, source_type, content, created
Function Call: notifications_get()
Buffers: shared hit=15015
Planning time: 0.045 ms
Execution time: 1091.698 ms
Run Code Online (Sandbox Code Playgroud)
589 毫秒。
显然,函数和查询之间的区别在于获取第一条记录所花费的时间为 99.5 毫秒。
我尝试了进一步的优化(可能是天真):
我面临的另一个问题是通过 id 获取记录的函数。0.25 毫秒与 0.025 毫秒。十倍的差异,但我或多或少地了解它的来源。同样,上面列出的优化技巧没有任何区别(似乎不应该)。
这(几乎)相当于问题中的函数,但执行起来就像一个简单的函数SELECT
:
CREATE OR REPLACE FUNCTION notifications_get_faster()
RETURNS SETOF private.notifications AS
$func$
SELECT * FROM private.notifications
$func$ LANGUAGE sql STABLE;
Run Code Online (Sandbox Code Playgroud)
几乎,因为它不是SECURITY DEFINER
,这会阻止预期的效果。
最值得注意的是,您将在查询计划中看到 aSeq Scan
而不是 the 。Function Scan
这就是造成大部分差异的原因。
您的各种尝试都不满足内联表函数的所有条件。这个功能就可以了。尤其:
函数是
LANGUAGE SQL
该函数不是
SECURITY DEFINER
该函数被声明
STABLE
或IMMUTABLE
因此 Postgres 可以获取函数体并执行它,而无需函数开销(“函数内联”)。与普通的SELECT
.
另外:不要引用语言名称。这是一个标识符。
归档时间: |
|
查看次数: |
940 次 |
最近记录: |