Der*_*ney 63 postgresql best-practices plpgsql
来自 MySQL 背景,其中存储过程性能(旧文章)和可用性值得怀疑,我正在评估 PostgreSQL 为我公司的新产品。
我想做的一件事是将一些应用程序逻辑移到存储过程中,所以我在这里询问有关在 PostgreSQL (9.0) 中使用函数的 DO 和 DON'T(最佳实践),特别是关于性能陷阱。
Erw*_*ter 71
严格来说,术语“存储过程”指的是 Postgres 中的SQL 过程,它是在 Postgres 11 中引入的。相关:
还有一些功能,几乎但不完全相同,从一开始就存在。
功能与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不能在事务块内运行,因此它们不允许在函数中使用。(但在 SQL 过程中,从 Postgres 11 开始。这可能会在以后添加。)
多年来,我已经编写了数千个 plpgsql 函数。
Nei*_*gan 15
一些 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?还是10,000,000?您构建应用程序和使用 UDF 的方式将在很大程度上取决于您尝试扩展的方式。如果您正在为成千上万的用户编写代码,那么您要做的主要事情就是尽可能地减少数据库的负载。减少移出和移回数据库的数据量的 UDF 将有助于减少 IO 负载。但是,如果它们开始增加 CPU 负载,那么它们可能会成为问题。一般来说,减少 IO 负载是第一要务,然后确保 UDF 高效,以免 CPU 过载。
| 归档时间: |
|
| 查看次数: |
42925 次 |
| 最近记录: |