JDBC 删除工作缓慢
JDBC Delete Works Slow
我在 sql 服务器上的 JDBC Delete 语句中遇到性能问题。
table 配置包含大约 700 万行,table 详细信息:
列:
- TERMINAL_ID (varchar(50))
- 属性(varchar(50))
- VALUE (nvarchar(1000))
索引:
TERMINAL_ID 和 ATTRIBUTE
上的聚簇唯一索引
代码如下所示,其中属性长度为 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
我在 sql 服务器上的 JDBC Delete 语句中遇到性能问题。 table 配置包含大约 700 万行,table 详细信息:
列:
- TERMINAL_ID (varchar(50))
- 属性(varchar(50))
- VALUE (nvarchar(1000))
索引: TERMINAL_ID 和 ATTRIBUTE
上的聚簇唯一索引代码如下所示,其中属性长度为 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