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

问题描述 投票:0回答:6

通过 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 不允许在会话池事务池之间来回切换,我们被迫在不同端口上运行两个单独的实例来满足我们的需求。

编辑2:

我遇到了这个链接,海报发布者在其中滚动了他自己的补丁。如果它被证明是安全有效的,我们目前正在考虑将其用于我们自己的用途。

java postgresql jdbc pgbouncer
6个回答
19
投票

禁用 JDBC 中的预准备语句。 对于 JDBC 来说,正确的方法是添加“prepareThreshold=0”参数来连接字符串。

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

8
投票

事实证明,这是使用会话池以外的任何其他方式时发生的 pgBouncer 问题。我们使用的是交易池,它显然不能支持准备好的语句。通过切换到会话池,我们解决了这个问题。

不幸的是,这对于我们的用例来说并不是一个好的解决方案。我们对 pgBouncer 有两种不同的用途:我们系统的一部分进行批量更新,这是作为准备好的语句最有效的,而另一部分则需要非常快速地连续进行许多连接。由于 pgBouncer 不允许在会话池事务池之间来回切换,我们被迫要么在不同的端口上运行两个单独的实例来支持我们的需求,要么实现这个补丁。初步测试表明它效果良好,但时间会证明它是否安全有效。


5
投票

新的、更好的答案

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

旧答案

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

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

我认为你有三个选择:

  1. 在 jdbc 驱动程序中禁用 PS,
  2. 在 Java 代码中手动释放它们,
  3. 配置 pgbouncer 在交易结束时丢弃它们。

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

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

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

或谷歌搜索:

postgresql jdbc +preparethreshold

1
投票

在我们的案例中,问题与 pgbouncer 无关。由于我们无法将

prepareThreshold=0
附加到 URL,我们所做的修复如下

查看准备好的报表

select * from pg_prepared_statements;

解除分配有问题的表

select * from pg_prepared_statements;
deallocate "S_1";

0
投票

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


0
投票

从 PgBouncer 1.21.0 开始,它现在支持名为准备好的语句的协议级别,这就是 JDBC 使用的内容。您可以通过将

max_prepared_statements
设置为非零值来打开此支持。有关详细信息,请查看文档:https://www.pgbouncer.org/config.html#max_prepared_statements

© www.soinside.com 2019 - 2024. All rights reserved.