JDBC 删除工作缓慢

JDBC Delete Works Slow

我在 sql 服务器上的 JDBC Delete 语句中遇到性能问题。 table 配置包含大约 700 万行,table 详细信息:

列:

  1. TERMINAL_ID (varchar(50))
  2. 属性(varchar(50))
  3. VALUE (nvarchar(1000))

索引: TERMINAL_IDATTRIBUTE

上的聚簇唯一索引

代码如下所示,其中属性长度为 1500,此程序需要大约 1 小时才能完成,删除速度非常慢:

     PreparedStatement statement = null;
     String sql = "DELETE FROM Config WHERE TERMINAL_ID = ? AND ATTRIBUTE = ?");
     for (String attribute : attributes) {
            if (statement == null) {
                statement = connection.prepareStatement(sqlDelete);
            }
            statement.setString(1, terminalId);
            statement.setString(2, attribute);
            statement.executeUpdate();
      }

当我在 Management Studio 中 运行 此查询 1500 次时,只需几秒钟即可删除。执行计划如下所示: 当行数较少时,问题就消失了。

当我使用 createStatement 而不是 prepareStatement 时,问题就出现了。

有什么想法吗?

尝试使用 preparedStatement.addBatch() 这可能会提高性能,

 PreparedStatement statement = null;
 String sql = "DELETE FROM Config WHERE TERMINAL_ID = ? AND ATTRIBUTE = ?");
     for (String attribute : attributes) {
            if (statement == null) {
                statement = connection.prepareStatement(sqlDelete);
            }
            statement.setString(1, terminalId);
            statement.setString(2, attribute);
            statement.addBatch();
      }
statement.executeBatch();
//commit 

尝试禁用此连接的自动提交,然后在完成所有删除语句后手动提交:

PreparedStatement statement = null;
String sql = "DELETE FROM Config WHERE TERMINAL_ID = ? AND ATTRIBUTE = ?");
try {
    connection.setAutoCommit(false);
    for (String attribute : attributes) {
        if (statement == null) {
            statement = connection.prepareStatement(sqlDelete);
        }
        statement.setString(1, terminalId);
        statement.setString(2, attribute);
        statement.executeUpdate();
    }
    connection.commit();
} finally {
    connection.setAutoCommit(true);
}

为简洁起见,我省略了异常处理。有关更详细的示例,请参阅 JDBC tutorial

将 prepareStatement 移到循环外。

 String sql = "DELETE FROM Config WHERE TERMINAL_ID = ? AND ATTRIBUTE = ?");
 PreparedStatement statement = connection.prepareStatement(sqlDelete);

 for (String attribute : attributes) {
        statement.setString(1, terminalId);
        statement.setString(2, attribute);
        statement.executeUpdate();
  }

我已经找到问题并解决了。问题出在方法 preparedStatement.setString() 中,它生成不同的查询并且执行计划不同。

我打开了SQL服务器Activity监控运行查询

从配置中删除 TERMINAL_ID = @P0 和属性 = @P1

所以我右键单击查询并打开如下所示的执行计划:

正如我所猜测的那样 SQL 服务器正在为每一行调用 CONVERT_IMPLICIT 函数并且正在聚集索引中进行扫描。该软件是第三方软件,所以我不得不将列更改为 nvarchar,问题就解决了。

我最近 运行 遇到了一个类似的问题,通过 JDBC 删除性能非常慢。

操作很简单:DELETE FROM t WHERE id=?

id 字段是主键。

但是,我在 perfmon 中注意到删除操作触发了完全扫描。

据我了解,潜在的问题是该列是 varchar 而不是 nvarchar,并且 JDBC 驱动程序正在重写 delete 以在查询期间对 id 进行隐式转换,而不是转换文字值。

此转换阻止查询使用索引。

当我将列切换为使用 nvarchar 时,问题就消失了。

Character data type conversion when using SQL Server JDBC drivers