38.11. 函数优化信息#
默认情况下,函数只是一个数据库系统对其行为知之甚少的“黑匣子”。但是,这意味着使用该函数的查询执行效率可能远低于预期。可以提供一些附加知识来帮助计划程序优化函数调用。
CREATE FUNCTION
命令中提供的声明性注释可以提供一些基本事实。其中最重要的是函数的波动性类别(IMMUTABLE
、STABLE
或VOLATILE
);定义函数时应始终小心正确指定此类别。如果您希望在并行查询中使用该函数,还必须指定并行安全性属性(PARALLEL UNSAFE
、PARALLEL RESTRICTED
或PARALLEL SAFE
)。指定函数的估计执行成本和/或集合返回函数估计返回的行数也可能很有用。但是,指定这两个事实的声明性方式只允许指定一个常量值,这通常是不够的。
还可以将规划器支持函数附加到 SQL 可调用函数(称为其目标函数),从而提供关于目标函数的知识,而这些知识过于复杂,无法通过声明方式表示。规划器支持函数必须用 C 编写(尽管其目标函数可能不是),因此这是一项高级功能,相对较少的人会使用。
规划器支持函数必须具有 SQL 签名
supportfn(internal) returns internal
在创建目标函数时通过指定SUPPORT
子句将其附加到其目标函数。
PostgreSQL源代码中的文件src/include/nodes/supportnodes.h
中可以找到规划器支持函数的 API 的详细信息。这里我们仅概述了规划器支持函数可以做什么。对支持函数的可能请求集是可扩展的,因此在未来版本中可能会实现更多功能。
根据函数的特定属性,可以在规划期间简化某些函数调用。例如,int4mul(n, 1)
可以简化为n
。这种类型的转换可以通过规划器支持函数执行,方法是让它实现SupportRequestSimplify
请求类型。将针对在查询解析树中找到的每个目标函数实例调用支持函数。如果它发现可以将特定调用简化为其他形式,则可以构建并返回表示该表达式的解析树。这也会自动适用于基于该函数的操作符——在刚刚给出的示例中,n * 1
也将简化为n
。(但请注意,这只是一个示例;此特定优化实际上并未由标准PostgreSQL执行。)我们不保证PostgreSQL永远不会在支持函数可以简化的案例中调用目标函数。确保简化表达式与目标函数的实际执行之间具有严格的等效性。
对于返回boolean
的目标函数,通常很有用的是估计将通过使用该函数的WHERE
子句选择的行数。这可以通过实现SupportRequestSelectivity
请求类型的支持函数来完成。
如果目标函数的运行时间高度依赖于其输入,则为其提供非恒定成本估算可能很有用。这可以通过实现SupportRequestCost
请求类型的支持函数来完成。
对于返回集合的目标函数,通常很有用的是为将返回的行数提供非恒定估算。这可以通过实现SupportRequestRows
请求类型的支持函数来完成。
对于返回boolean
的目标函数,可以将出现在WHERE
中的函数调用转换为可索引运算符子句或子句。转换后的子句可能与函数条件完全等效,或者它们可能稍弱一些(即,它们可能接受函数条件不接受的一些值)。在后一种情况下,索引条件被称为有损;它仍然可用于扫描索引,但必须为索引返回的每一行执行函数调用,以查看它是否真正通过WHERE
条件。要创建此类条件,支持函数必须实现SupportRequestIndexCondition
请求类型。