【问题标题】:What's the life span of a PostgreSQL server-side prepared statementPostgreSQL 服务器端准备好的语句的生命周期是多少
【发布时间】:2015-11-24 16:14:33
【问题描述】:

根据PostgreSQL documentation,准备好的语句绑定到数据库会话/连接:

PREPARE 创建一个准备好的语句。准备好的语句是 可用于优化性能的服务器端对象。当。。。的时候 执行 PREPARE 语句,解析指定语句, 分析,重写。当一个 EXECUTE 命令随后 发出,准备好的语句被计划和执行。

准备好的语句只持续当前数据库的持续时间 会议。会话结束时,准备好的语句被遗忘, 所以在再次使用之前必须重新创建它。

但是,Markus Winand (author of SQL Performance Explained) says that:

PostgreSQL 没有共享查询计划缓存,但它有一个 准备好的语句的可选查询计划缓存。这意味着 开发人员可以选择是否使用准备好的语句 缓存查询计划。但请注意,缓存被丢弃时 准备好的语句已关闭。

哪一个是真的?

只要数据库连接打开,准备好的语句是否存在,所以当使用连接池时,只要池没有显式关闭物理连接或服务器端准备好的语句被清除,它就可以存在一旦 JDBC PreparedStatement 关闭。

【问题讨论】:

    标签: database postgresql session jdbc prepared-statement


    【解决方案1】:

    所以,您的问题最终归结为“java.sql.PreparedStatement 如何使用 PostgreSQL”。最后请参阅“这如何与服务器准备的计划一起使用”的答案。

    答案如下:这取决于您使用的 JDBC 驱动程序。

    TL;DR:在现代驱动程序中,服务器准备的语句一直存在,直到连接终止或语句被另一个语句驱逐(常规 LRU 驱逐)。

    注意:PostgreSQL 服务器不能跨数据库连接共享准备好的语句,因此最好的 JDBC 驱动程序可以做的是在每个连接中保持计划缓存。

    注意:JDBC 规范要求将 ?, ? 用于绑定占位符,而服务器需要 $1, $2,因此 JDBC 驱动程序也会缓存所谓的已解析 SQL 文本。

    有两个著名的 JDBC 驱动程序:pgjdbc 和 pgjdbc-ng

    pgjdbc

    https://github.com/pgjdbc/pgjdbc

    由于pgjdbc 9.4-1202,它在使用PreparedStatement 时会自动缓存服务器端计划。 注意:即使您close() PreparedStatement,这些语句也会被缓存。 为了进入服务器端准备,您需要执行 5 次查询(可以通过 prepareThreshold 配置)。

    目前,缓存是按连接实现的。默认情况下,pgjdbc 缓存 256 (preparedStatementCacheQueries) 个查询和最多 preparedStatementCacheSizeMiB 个查询。这是一个保守的设置,因此您可能需要对其进行调整。有关属性的描述,请参阅documentation。 缓存包括解析语句和服务器准备语句。

    github问题:https://github.com/pgjdbc/pgjdbc/pull/319

    pgjdbc-ng

    https://github.com/impossibl/pgjdbc-ng

    我不喜欢 pgjdbc-ng,但它看起来既可以解析(默认缓存大小为 250 查询)和服务器准备(默认缓存大小为 50 查询)。对服务器端预处理语句的支持于 2014 年 2 月 24 日登陆,因此如果您使用较新的版本,您可以获得语句缓存。

    注意:如果您不小心使用了很长的查询,您可以点击OutOfMemory,因为 pgjdbc-ng 无法根据保留字节数驱逐条目。

    缓存是每个连接的,因此即使您关闭语句,它也会被透明地使用。

    关于 pgjdbc-ng 的性能我不能说太多,尽管自从上次我尝试向它抛出 jmh 时它失败并出现随机异常。

    github问题:https://github.com/impossibl/pgjdbc-ng/pull/69

    服务器准备的计划

    PostgreSQL 有PREPAREDEALLOCATE 命令在通过网络发送EXEC 时引用该语句。它优化了两件事:

    1. 当使用PREPAREd 语句(换句话说,服务器准备的语句)时,客户端不必一次又一次地发送查询文本。它只是发送一个简短的查询名称和绑定变量的值。
    2. 自 9.2 起,数据库仍会尝试重新计划查询的前几次执行。这样做是为了尝试查询是否需要多个计划或通用计划是否足够好。最终(如果查询没有参数,则立即),数据库might switch to a generic plan
    3. 从 12 开始,有一个设置可以强制使用通用或自定义计划执行所有服务器准备的语句:plan_cache_mode=auto | force_custom_plan | force_generic_plan

    换句话说,PreparedStatement 优化了 JDBC 端的查询解析和数据库端的查询规划。

    更多信息在这里:http://blog.endpoint.com/2014/04/custom-plans-prepared-statements-in.html

    PL/pgSQL 中的准备语句

    根据文档,PostgreSQL caches 计划用于 PL/pgSQL 中的查询。这发生在几次执行之后(3 或 5,我不记得确切的阈值),所以在创建存储过程之后它可能会有点慢,但是它会切换到缓存计划(前提是数据库同意使用通用计划用于特定查询)。

    换句话说,为了实现“缓存执行计划”,您要么需要使用最新的 JDBC 驱动程序,要么可以将所有查询包装到存储过程中。 对过程的调用将在每次执行时重新计划,但调用本身通常比构成过程的查询短得多。

    【讨论】:

    • 感谢您的回答,我看到您在 9.4 上一直在开发此功能。因此,这意味着即使在 PreparedStatement 关闭后,准备好的语句也可用于当前连接。这是个好消息,因为它允许对频繁执行的语句重用解析/编译。
    • 另一个注意事项:自 9.2 起,PostgreSQL 不再使用 PREPARE 调用创建执行计划——实际计划仅在 EXECUTE 时间完成(在@给出的文档的引用中明确说明VladMihalcea。这意味着:所有的缓存都只是缓存重写的查询,而不是执行计划。
    • 感谢您的提示。缓存的是解析、重写和编译。关联的执行计划推迟到执行时间。
    • @MarkusWinand,你又有点跑题了。最终 PostgreSQL 将缓存执行计划,因此使用准备好的语句确实可以提高执行速度。
    【解决方案2】:

    我认为两者都是正确的,但怀疑 PostgreSQL 文档通常比我更真实。但是,这里我认为 PostgreSQL 文档可能不准确。

    应该是这样的:

    准备好的语句只持续到DEALLOCATEed,并且不超过当前数据库会话的持续时间。

    如果不检查,我坚信 JDBC 驱动程序会在 JDBC PreparedStatement 关闭时释放服务器端准备好的语句。

    【讨论】:

    • 谢谢。我也必须将这个问题添加到 PG 邮件列表中。
    • pgdjbcpgjdbc-ng 都在 close() 调用中缓存语句,因为许多应用程序都是以 prepare-execute-close 方式编写的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-31
    • 2016-08-26
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多