Postgres - 错误:预准备语句“S_1”已存在新的,更好的答案旧答案

2022-09-01 07:50:45

当通过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:

我碰到了这个链接,海报上已经翻了一块他自己的补丁。我们目前正在考虑将其实现为我们自己的用途,如果它被证明是安全有效的。


答案 1

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

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

答案 2

新的,更好的答案

要放弃会话状态并有效地忘记“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

推荐