Postgres - 错误:准备好的语句“S_1”已经存在

Posted

技术标签:

【中文标题】Postgres - 错误:准备好的语句“S_1”已经存在【英文标题】:Postgres - ERROR: prepared statement "S_1" already exists 【发布时间】:2011-11-28 13:00:30 【问题描述】:

通过 JDBC 对 pgbouncer 执行批量查询时,出现以下错误:

org.postgresql.util.PSQLException: ERROR: prepared statement "S_1" already exists

我在网上发现了一些错误报告,但它们似乎都在处理 Postgres 8.3 或更低版本,而我们正在使用 Postgres 9。

这是触发错误的代码:

this.getJdbcTemplate().update("delete from xx where username = ?", username);

this.getJdbcTemplate().batchUpdate( "INSERT INTO xx(a, b, c, d, e) " + 
                "VALUES (?, ?, ?, ?, ?)", new BatchPreparedStatementSetter() 
    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException 
        ps.setString(1, value1);
        ps.setString(2, value2);
        ps.setString(3, value3);
        ps.setString(4, value4);
        ps.setBoolean(5, value5);
    
    @Override
    public int getBatchSize() 
        return something();
    
);

有人见过这个吗?

编辑 1:

结果证明这是使用 会话池 以外的任何东西时出现的 pgBouncer 问题。我们使用的是事务池,它显然不支持准备好的语句。通过切换到会话池,我们解决了这个问题。

很遗憾,这对我们的用例来说不是一个好的解决方案。我们对 pgBouncer 有两种不同的用途:我们系统的一部分进行批量更新,这作为准备好的语句最有效,另一部分需要非常快速的连续连接。由于 pgBouncer 不允许在 session poolingtransaction pooling 之间来回切换,我们不得不在不同的端口上运行两个单独的实例来满足我们的需求。

编辑 2:

我遇到了this link,发帖人已经在其中滚动了自己的补丁。如果它被证明是安全有效的,我们目前正在考虑实施它以供我们自己使用。

【问题讨论】:

可能是 pgbouncer 问题?还是过时的 JDBC 驱动程序? 绝对是 pgbouncer 的问题。我们绕过 pgbouncer 直接点击 Postgres,prepared 语句就可以工作了。 JDBC 驱动程序是最新可用的。 是否有一些设置告诉 pgbouncer 在释放回池时重置连接?听起来您正在重用之前已经运行过准备的连接。 @ScottMarlowe 有。这是连接池选项。我们使用transaction pooling,它应该在事务结束后立即将连接释放回池。 这不是释放连接,而是重置它。如果它没有对连接进行重置,那么像准备好的查询这样的旧东西仍然可以存在。 【参考方案1】:

在 JDBC 中禁用准备好的语句。 为 JDBC 执行此操作的正确方法是在连接字符串中添加“prepareThreshold=0”参数。

jdbc:postgresql://ip:port/db_name?prepareThreshold=0

【讨论】:

为我工作 - 但也许从你的答案中删除 useAffectedRows=true 会更好?没必要? 好的,刚刚删除了【参考方案2】:

新的更好的答案

要丢弃会话状态并有效地忘记 "S_1" 准备好的语句,请在 PgBouncer 配置中使用 server_reset_query 选项。

旧答案

见http://pgbouncer.projects.postgresql.org/doc/faq.html#_how_to_use_prepared_statements_with_transaction_pooling

切换到会话模式并不是一个理想的解决方案。 Transacion pooling 效率更高。但是对于事务池,您需要无状态的数据库调用。

我认为你有三个选择:

    在 jdbc 驱动程序中禁用 PS, 在您的 Java 代码中手动解除分配, 配置 pgbouncer 以在事务结束时丢弃它们。

我会尝试选项 1 或选项 3 - 取决于您的应用使用它们的实际方式。

有关更多信息,请阅读文档:

http://pgbouncer.projects.postgresql.org/doc/config.html(搜索 server_reset_query),

或者谷歌这个:

postgresql jdbc +preparethreshold

【讨论】:

"使用事务池时,server_reset_query 应该为空,因为客户端不应使用任何会话功能。"看起来选项 3 是不可能的。选项 1 不适合我们使用。选项 2 可能可行,但如何实现? 看来session_lifetime 可能是导致问题的设置。我们现在正在试验它。我会尽快回来报告。 @Chris:选项 3 是可能的 - pgbouncer 文档说一般应该做什么,但在你的情况下,我会尝试。关于选项 2,请参阅 commandprompt.com/ppbook/x20921,关于选项 1 - 为什么不呢? server_reset_query 文档的新链接:pgbouncer.github.io/faq.html#what-should-my-serverresetquery-be【参考方案3】:

结果证明这是使用 会话池 以外的任何东西时出现的 pgBouncer 问题。我们使用的是事务池,它显然不支持准备好的语句。通过切换到会话池,我们解决了这个问题。

很遗憾,这对我们的用例来说并不是一个好的解决方案。我们对 pgBouncer 有两种不同的用途:我们系统的一部分进行批量更新,这作为准备好的语句最有效,另一部分需要非常快速的连续连接。由于 pgBouncer 不允许在 session poolingtransaction pooling 之间来回切换,我们不得不在不同的端口上运行两个单独的实例来满足我们的需求,或实施this patch。初步测试表明它运作良好,但时间会证明它是否安全有效。

【讨论】:

链接失效了,很遗憾。这是存档快照:web.archive.org/web/20120222062130/http://pgfoundry.org/…【参考方案4】:

我遇到了这个问题,我们在事务级别配置了 pgbouncer,我们使用的是 psql 11.8,我们刚刚将 psql jar 升级到最新版本,它得到了修复。

【讨论】:

请发布用户询问的特定版本的 psql 的答案,因为升级 psql 可能会导致其他问题并且用户难以理解。并尝试更正/即兴发布有问题的代码 感谢您的反馈! - Dilip D 我已经从 postgres 8.4 升级到 postgres.jdbc.4.2-42..2.14【参考方案5】:

在我们的案例中,问题与 pgbouncer 无关。由于我们无法将 prepareThreshold=0 附加到 URL,因此我们所做的修复如下

查看准备好的语句

select * from pg_prepared_statements;

释放故障表

select * from pg_prepared_statements;
deallocate "S_1";

【讨论】:

以上是关于Postgres - 错误:准备好的语句“S_1”已经存在的主要内容,如果未能解决你的问题,请参考以下文章

构造准备好的语句时如何处理可选列

如何摆脱 MySQL 错误“需要重新准备准备好的语句”

使用 PDO 准备好的语句 MySQL 错误

准备好的语句语法错误

使用 PDO 准备好的语句插入致命错误 [重复]

准备好的语句失败(带有错误消息!)