即使在 rewriteBatchedStatements = true 之后,mysql jdbc 也不会批处理查询

Posted

技术标签:

【中文标题】即使在 rewriteBatchedStatements = true 之后,mysql jdbc 也不会批处理查询【英文标题】:mysql jdbc not batching queries even after rewriteBatchedStatements = true 【发布时间】:2017-11-13 08:06:24 【问题描述】:

我一直在阅读整个互联网 + ***,了解为什么 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方法中的一个sn-p,看起来像这样,你可以看到它调用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();

这是我要插入的表格

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();

这是我pom中jdbc的版本

<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 并且它有效。

【问题讨论】:

请阅读此内容,尤其是查询性能部分。 meta.***.com/a/271056 那么请edit 您的问题提供更多信息。同时,请弄清楚您是否打开了自动提交,或者您是否在 SQL 中的 BEGIN TRANSACTION / COMMIT 块中包装了多个 INSERT 语句。 谢谢。我编辑了标题,因为我并不真正关心特定的查询性能,它应该很快,因为它只是一个插入,而且我不认为这是一个典型的“为什么我的查询很慢”的问题我更感兴趣的是为什么rewritebatchedstatements = true 没有得到尊重并正确批处理插入。我已经添加了表结构和自动提交,并且它目前已打开 使用addBatch()executeBatch() 方法的代码在哪里?重写带来的性能提升来自于使用批处理执行多行插入,从而减少了数据库服务器执行的隐式COMMIT 操作的数量。在打电话给executeBatch() 之前,你是不是打电话给addBatch() 一大堆? docs.oracle.com/javase/7/docs/api/java/sql/… 是的,我是。我添加了我在第一个示例中使用的 jdbcTemplate.batchUpdate() 实现的 sn-p,该方法调用 addBatch() 和 executeBatch()。我还添加了另一个我尝试过的 sn-p,我在其中显式调用 addBatch() 和 executeBatch() 而不使用内置的 JdbcTemplate 方法 batchUpdate() 【参考方案1】:

对于遇到 jdbcTemplate 连接 url 不支持 rewriteBatchedStatements = true 问题的其他人,请检查 pom.xml 中的 mysql-connector-java 版本

在写这个问题的时候我有

<dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
</dependency>

由于我的批量写入是一一发生的,因此版本 5.1.9 似乎不支持批量更新,并且如 spring docs 中所述回退

batchUpdate() -- 如果 JDBC 驱动程序不支持批量更新,将退回到在单个语句上单独更新。

将此更新到版本 5.1.18 给了我适当的批量更新,并在 mysql 常规日志中验证。

也是我遇到的一个错误,它可能会为其他人节省一些时间。在 5.1.23 版本中,当您将 db url 配置为包含 profileSQL=true(我想大多数人都会这样做)时,会有一个带有驱动程序和 profileSQL 的 bug。

【讨论】:

以上是关于即使在 rewriteBatchedStatements = true 之后,mysql jdbc 也不会批处理查询的主要内容,如果未能解决你的问题,请参考以下文章

即使屏幕关闭,如何在后台运行代码?

即使在采取响应操作后,UIAlertView 也不会消失

为啥即使在 System.exit(0) 之后也需要返回;

Tensorflow:即使在关闭会话时内存泄漏?

即使我阻止横向旋转,屏幕也在旋转

Xcode 测试:清理代码,即使停止