由于MySQL的背景,存储过程的性能(较旧的文章)和可用性令人怀疑,我正在为我的公司评估PostgreSQL的新产品。
我想做的一件事是将一些应用程序逻辑移到存储过程中,因此我在这里要求在PostgreSQL(9.0)中使用函数时应做的DO和不做的(最佳实践),特别是关于性能陷阱。
由于MySQL的背景,存储过程的性能(较旧的文章)和可用性令人怀疑,我正在为我的公司评估PostgreSQL的新产品。
我想做的一件事是将一些应用程序逻辑移到存储过程中,因此我在这里要求在PostgreSQL(9.0)中使用函数时应做的DO和不做的(最佳实践),特别是关于性能陷阱。
Answers:
严格来说,术语“存储过程”指向Postgres 11中引入的Postgres中的SQL过程。
还有一些功能,几乎可以完成,但不完全相同,而这些功能从一开始就存在。
功能与LANGUAGE sql
基本上与一个函数包装纯SQL命令(以及因此原子,总是一个内运行刚批处理文件单个事务)接受参数。SQL函数中的所有语句都是立即计划的,这与在另一个语句之后执行一个语句有细微的差别,并且可能会影响获取锁的顺序。
除此之外,最成熟的语言是PL / pgSQL(LANGUAGE plpgsql
)。它运行良好,并且在过去十年中的每个发行版中都得到了改进,但它最适合用作SQL命令的粘合剂。它不用于繁重的计算(SQL命令除外)。
PL / pgSQL函数执行查询,如准备好的语句。重用缓存的查询计划可以减少一些计划开销,并使它们比等效的SQL语句快一点,这可能会因情况而产生明显的影响。它也可能有类似以下相关问题的副作用:
此携带的优点和准备语句的缺点- 在手动讨论。对于具有不规则数据分布和可变参数的表的查询,当针对给定参数的优化执行计划获得的收益超过重新计划的成本时,动态SQL的性能EXECUTE
可能会更好。
由于Postgres 9.2的通用执行计划仍在会话中缓存,因此请引用手册:
对于没有参数的准备好的语句,这种情况立即发生;否则,只有在五个或更多执行生成计划的估算平均成本(包括计划间接费用)比通用计划成本估算贵得多的计划之后,才会发生这种情况。
在大多数情况下,我们在不(ab)使用的情况下(最好不要花费额外的开销)两全其美EXECUTE
。PostgreSQL Wiki的PostgreSQL 9.2中的新增功能中的详细信息。
Postgres 12引入了附加服务器变量plan_cache_mode
来强制执行通用或自定义计划。对于特殊情况,请小心使用。
服务器端的功能可以防止应用程序与数据库服务器进行额外的往返,从而赢得巨大的成功。让服务器一次执行尽可能多的操作,仅返回定义良好的结果。
避免嵌套复杂的函数,尤其是表函数(RETURNING SETOF record
或TABLE (...)
)。函数是黑匣子,构成了查询计划者的优化障碍。它们是分开优化的,而不是在外部查询的上下文中进行的,这使计划变得更简单,但可能会导致计划不尽人意。而且,功能的成本和结果大小无法可靠地预测。
此规则的例外是简单的SQL函数(LANGUAGE sql
),如果满足某些先决条件,则可以“内联”。在Neil Conway的这篇演讲中,了解有关查询计划器工作原理的更多信息(高级知识)。
在PostgreSQL中,函数总是自动在单个事务中运行。所有的一切都成功了,还是没有成功。如果发生异常,则所有内容都会回滚。但是有错误处理 ...
这就是为什么函数不完全是“存储过程”的原因(即使有时会误用该术语)。一些命令喜欢VACUUM
,CREATE INDEX CONCURRENTLY
或CREATE DATABASE
不能在事务块内运行,因此它们没有在功能不允许的。(从Postgres 11开始,SQL过程中都没有。以后可能会添加。)
这些年来,我已经编写了数千个plpgsql函数。
一些DO:
您可以使用postgresql中的用户定义函数(UDF)做一些非常有趣的事情。例如,您可以使用多种语言。内置的pl / sql和pl / pgsql既强大又可靠,并使用沙盒方法来阻止用户执行任何非常危险的事情。用C编写的UDF在与数据库本身相同的上下文中运行,从而为您提供了最大的功能和性能。但是,这就像在玩火,因为即使是很小的错误也会导致巨大的问题,后端崩溃或数据损坏。定制pl语言(例如pl / R,pl / ruby,pl / perl等)使您能够用相同的语言编写数据库层和应用程序层。这可能很方便,因为这意味着您不必教Perl程序员Java或pl / pgsql等来编写UDF。
最后,有pl / proxy语言。使用此UDF语言,您可以在数十个或更多后端Postgresql服务器上运行应用程序以进行扩展。它是由Skype的好人开发的,基本上允许穷人的水平缩放解决方案。写起来也很容易。
现在,关于性能问题。这是灰色区域。您是否正在为一个人编写应用程序?还是1000?还是一千万?构建应用和使用UDF的方式将取决于您尝试扩展的方式。如果您要为成千上万的用户编写程序,那么您要做的主要事情就是尽可能减少db的负载。UDF可以减少移出和移回数据库的数据量,这将有助于减少IO负载。但是,如果它们开始增加CPU负载,则可能是一个问题。一般来说,降低IO负载是第一要务,其次是确保UDF高效以免使CPU过载。