声明函数波动性 IMMUTABLE 会损害性能吗?

Erw*_*ter 9 postgresql performance functions

Postgres 函数声明为波动性分类VOLATILESTABLEIMMUTABLE。众所周知,该项目对内置函数的这些标签非常严格。并且有充分的理由。突出的例子:表达式索引只允许IMMUTABLE函数并且那些必须是真正不可变的以避免错误的结果。

用户定义的函数仍然可以按照所有者的选择自由声明。手册建议:

为了获得最佳优化结果,您应该使用对它们有效的最严格的波动率类别来标记您的函数。

...并添加了大量可能因波动率标签不正确而出错的事情列表。

尽管如此,在某些情况下,伪造不变性是有道理的。大多数情况下,当您知道该函数实际上在您的范围内是不可变的。例子:

除了对数据完整性的所有可能影响之外,对性能的影响是什么?人们可能认为声明一个函数IMMUTABLE只会对性能有益。是这样吗?

声明函数波动性会IMMUTABLE 损害性能吗?

让我们假设当前的 Postgres 10 来缩小范围,但所有最近的版本都很有趣。

Erw*_*ter 9

是的,它损害性能。

可以在调用查询中“内联”简单的 SQL 函数。引用 Postgres Wiki

LANGUAGE SQL在某些条件下,SQL 函数(即)会将它们的函数体内联到调用查询中,而不是直接调用。这可以带来显着的性能优势, 因为函数体会暴露给调用查询的规划器,后者可以应用诸如常量折叠、质量下推等优化。

大胆强调我的。

为了确保正确性,有许多先决条件。其中之一

如果函数已声明IMMUTABLE,则表达式不得调用任何非不可变函数或运算符

这意味着,使用任何非不可变函数但仍被声明的 SQL 函数IMMTUTABLE被排除在此优化之外。由关于 SO 的这些相关答案触发,我一直在进行广泛的测试:

基本上比较简单 SQL 函数的这两个变体(将日期映射到integer,忽略与目的无关的年份):

CREATE FUNCTION f_mmdd_tc_s(date) RETURNS int LANGUAGE sql STABLE    AS
$$SELECT to_char($1, 'MMDD')::int$$;

CREATE FUNCTION f_mmdd_tc_i(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT to_char($1, 'MMDD')::int$$;  -- cannot be inlined!
Run Code Online (Sandbox Code Playgroud)

Postgres 函数to_char()只是STABLE,不是IMMUTABLE(它的所有重载实例 -原因超出了本答案的范围)。所以第二个是假的IMMUTABLE,结果在一个简单的测试中5 倍

db<>在这里摆弄

这个特定的例子可以替换为等效的:

CREATE FUNCTION f_mmdd(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT (EXTRACT(month FROM $1) * 100 + EXTRACT(day FROM $1))::int$$;
Run Code Online (Sandbox Code Playgroud)

好像有两个函数调用和更多的计算更加昂贵。但是IMMUTABLE标签是真实的(另外,使用的函数速度更快,强制text转换integer也更昂贵)。

比上述较快变体快 2倍(比较慢变体快 10 倍)。关键是:在可能的情况下使用IMMUTABLE函数,然后您就不必“作弊”一开始。