mysql jdbc 即使在 rewriteBatchedStatements = true 之后也不批处理查询
mysql jdbc not batching queries even after rewriteBatchedStatements = true
我一直在整个互联网 + Whosebug 上阅读有关 jdbc 批量更新为何如此缓慢的原因。看起来正确的解决方法是在连接字符串中设置 rewriteBatchedStatements = true
。但我似乎无法让它为我工作。
我正在使用 springboot 和 spring-jdbc
我在 application.properties
中设置了 rewriteBatchedStatements = true
spring.datasource.url=jdbc:mysql://RDS_URL.us-west-2.rds.amazonaws.com/DATABASE?rewriteBatchedStatements=true
我还设置了一个断点来验证 ?rewriteBatchedStatements=true
是否反映在代码中
我已将 general_log 设置为 true,在查看日志时我发现插入内容未正确批处理
这就是我的 sql 字符串的样子
private static String INSERT_USER_TO_GROUP_SQL = "INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES(?, ?, ?, ?)";
日志中的行都是这样的
45 Query INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('49', '99999999999', '123', 'web')
我的 java 批量插入代码是
executor.submit(() -> {
jdbcTemplate.batchUpdate(INSERT_USER_TO_GROUP_SQL, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Subscriber subscriber = subscribers.get(i);
ps.setString(1, subscriberGroup.getGroupId());
ps.setString(2, subscriber.getPhoneNumber());
ps.setString(3, accountId);
ps.setString(4, subscriberGroup.getSource());
}
@Override
public int getBatchSize() {
return subscribers.size();
}
}); // end BatchPreparedStatementSetter lambda class
}); // end thread
这是方法 batchUpdate
的一个片段,看起来像这样,正如您所看到的,它调用了 addBatch(),最后调用了 executeBatch()
for (int i = 0; i < batchSize; i++) {
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
ps.addBatch();
}
return ps.executeBatch();
这是我要插入的 table
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`phoneNumber` varchar(20) DEFAULT NULL,
`groupId` varchar(11) DEFAULT NULL,
`source` varchar(30) DEFAULT NULL,
`accountId` varchar(50) DEFAULT NULL,
`deleted` int(1) DEFAULT '0',
`timestamp` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `phoneNumber` (`phoneNumber`,`groupId`,`accountId`)
) ENGINE=InnoDB AUTO_INCREMENT=21677 DEFAULT CHARSET=latin1;
此外,我什至尝试过不依赖 jdbc.batchUpdate() 并自己完成。仍然没有运气
Connection connection = jdbcTemplate.getDataSource().getConnection();
connection.setAutoCommit(false);
PreparedStatement preparedStatement =
connection.prepareStatement(INSERT_USER_TO_GROUP_SQL);
preparedStatement.setString(1, "1");
preparedStatement.setString(2, "2");
preparedStatement.setString(3, "3");
preparedStatement.setString(4, "4");
preparedStatement.addBatch();
preparedStatement.setString(1, "11");
preparedStatement.setString(2, "22");
preparedStatement.setString(3, "33");
preparedStatement.setString(4, "44");
preparedStatement.addBatch();
preparedStatement.executeBatch();
connection.commit();
我还试图排除准备好的语句的问题,所以我只尝试硬编码查询。仍然没有运气。
Connection connection = jdbcTemplate.getDataSource().getConnection();
Statement statement = connection.createStatement();
statement.addBatch("INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('1', '2', '3', '4')");
statement.addBatch("INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('11', '22', '33', '44')");
statement.executeBatch();
这是我 pomjdbc 中的版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
我希望此参数能够加快插入速度并让日志显示正确的批处理插入语句。大多数 SO 文章显示人们只需在 url 中设置 rewritebatchedstatements = true
并且它有效。
对于其他遇到 jdbcTemplate 连接问题的人 url 不遵守 rewriteBatchedStatements = true
检查 mysql-connector-java
版本 pom.xml
在写这个问题的时候我
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
由于我的批量写入是一个接一个地进行的,因此版本 5.1.9 似乎不支持批量更新,并且如 spring docs
中所述退回
batchUpdate() -- Will fall back to separate updates on a single Statement if the JDBC driver does not support batch updates.
将此升级到 5.1.18 版后,我得到了正确的批量更新,已在 mysql 通用日志中验证。
还有一个我 运行 可能会节省其他人时间的错误。在 5.1.23 版中,当您配置 db url 以包含 profileSQL=true
(我想大多数人都会这样做)时,驱动程序和 profileSQL 会出现 bug。
我一直在整个互联网 + Whosebug 上阅读有关 jdbc 批量更新为何如此缓慢的原因。看起来正确的解决方法是在连接字符串中设置 rewriteBatchedStatements = true
。但我似乎无法让它为我工作。
我正在使用 springboot 和 spring-jdbc 我在 application.properties
中设置了rewriteBatchedStatements = true
spring.datasource.url=jdbc:mysql://RDS_URL.us-west-2.rds.amazonaws.com/DATABASE?rewriteBatchedStatements=true
我还设置了一个断点来验证 ?rewriteBatchedStatements=true
是否反映在代码中
我已将 general_log 设置为 true,在查看日志时我发现插入内容未正确批处理
这就是我的 sql 字符串的样子
private static String INSERT_USER_TO_GROUP_SQL = "INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES(?, ?, ?, ?)";
日志中的行都是这样的
45 Query INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('49', '99999999999', '123', 'web')
我的 java 批量插入代码是
executor.submit(() -> {
jdbcTemplate.batchUpdate(INSERT_USER_TO_GROUP_SQL, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Subscriber subscriber = subscribers.get(i);
ps.setString(1, subscriberGroup.getGroupId());
ps.setString(2, subscriber.getPhoneNumber());
ps.setString(3, accountId);
ps.setString(4, subscriberGroup.getSource());
}
@Override
public int getBatchSize() {
return subscribers.size();
}
}); // end BatchPreparedStatementSetter lambda class
}); // end thread
这是方法 batchUpdate
的一个片段,看起来像这样,正如您所看到的,它调用了 addBatch(),最后调用了 executeBatch()
for (int i = 0; i < batchSize; i++) {
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
ps.addBatch();
}
return ps.executeBatch();
这是我要插入的 table
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`phoneNumber` varchar(20) DEFAULT NULL,
`groupId` varchar(11) DEFAULT NULL,
`source` varchar(30) DEFAULT NULL,
`accountId` varchar(50) DEFAULT NULL,
`deleted` int(1) DEFAULT '0',
`timestamp` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `phoneNumber` (`phoneNumber`,`groupId`,`accountId`)
) ENGINE=InnoDB AUTO_INCREMENT=21677 DEFAULT CHARSET=latin1;
此外,我什至尝试过不依赖 jdbc.batchUpdate() 并自己完成。仍然没有运气
Connection connection = jdbcTemplate.getDataSource().getConnection();
connection.setAutoCommit(false);
PreparedStatement preparedStatement =
connection.prepareStatement(INSERT_USER_TO_GROUP_SQL);
preparedStatement.setString(1, "1");
preparedStatement.setString(2, "2");
preparedStatement.setString(3, "3");
preparedStatement.setString(4, "4");
preparedStatement.addBatch();
preparedStatement.setString(1, "11");
preparedStatement.setString(2, "22");
preparedStatement.setString(3, "33");
preparedStatement.setString(4, "44");
preparedStatement.addBatch();
preparedStatement.executeBatch();
connection.commit();
我还试图排除准备好的语句的问题,所以我只尝试硬编码查询。仍然没有运气。
Connection connection = jdbcTemplate.getDataSource().getConnection();
Statement statement = connection.createStatement();
statement.addBatch("INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('1', '2', '3', '4')");
statement.addBatch("INSERT INTO users (groupId, phoneNumber, accountId, source) VALUES('11', '22', '33', '44')");
statement.executeBatch();
这是我 pomjdbc 中的版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
我希望此参数能够加快插入速度并让日志显示正确的批处理插入语句。大多数 SO 文章显示人们只需在 url 中设置 rewritebatchedstatements = true
并且它有效。
对于其他遇到 jdbcTemplate 连接问题的人 url 不遵守 rewriteBatchedStatements = true
检查 mysql-connector-java
版本 pom.xml
在写这个问题的时候我
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
由于我的批量写入是一个接一个地进行的,因此版本 5.1.9 似乎不支持批量更新,并且如 spring docs
中所述退回batchUpdate() -- Will fall back to separate updates on a single Statement if the JDBC driver does not support batch updates.
将此升级到 5.1.18 版后,我得到了正确的批量更新,已在 mysql 通用日志中验证。
还有一个我 运行 可能会节省其他人时间的错误。在 5.1.23 版中,当您配置 db url 以包含 profileSQL=true
(我想大多数人都会这样做)时,驱动程序和 profileSQL 会出现 bug。