rewriteBatchedStatements参数
rewriteBatchedStatements是一个Mysql的参数
原理
批处理的思想
没有配置该参数的时候,一条更新(update、insert)就代表了向Mysql发送一条语句
可以实现多条更新语句合并提交给mysql,他会把多条更新语句,拼接成一条语句发送给mysql
效果对比
如何开启
在数据库连接中的设置URL把参数加上即可
查看日志
没有报错信息
故障排查过程
经过排查,发现是业务更新数据库走的是jdbcTemplate.batchUpdate,并且加了rewriteBatchedStatements这个参数才导致更新失败
那么我们仔细看看batchUpdate这个方法。
当我们继续往里面跟,就会直接去到第三方源码里面了:
JdbcTemplate.batchUpdate
这边我们可以很清晰地看到在Spring的JdbcTemplate是注册了一个PreparedStatement回调函数。
这个回调函数其实就是Spring用来接受数据库语句执行后的结果。
我们再进下一层ps.executeBatch
PreparedStatement.executeBatch
看到这里其实已经能看到我们这个参数rewriteBatchedStatements判断是否开启的地方了
抑或是我们可以通过直接搜索这个参数,去看引用他的地方,也非常好定位
同样也能定位到刚刚那个地方。
我们可以看到这个参数要起作用还要满足一些条件:
- 版本号要大于
4.1.0 - 要有更新数据
- 并且数量要大于3条sql
否则还是去走一条一条sql。
到这里,我们再进下一层executePrepareBatchAsMultiStatement
PreparedStatement.executePrepareBatchAsMultiStatement
看到开头我就非常敏感,他这里是直接拼接一个分号“;”到语句里面。而且我们看到他的注释:
“这是一种比较滥用的方式,但是确实能够解决问题”
异常出现的地方
总结
因为框架里面会给我们的语句添加一个分号 ; 如果我们在原语句也有分号,那么就会有2个分号,引起语法错误
- 加上参数
rewriteBatchedStatements能很大程度上提高我们对数据批量写入的能力 - 注意:原sql语句结尾不要带分号;
剩余问题
- 为什么没有报错信息?
- 为什么批量更新只有一行数据能更新成功?
问题1:无报错信息
这边是捕获DataACcessEXception异常,我看可以看看他的继承关系图:
然后我们去看看框架里面batchUpdate,捕获的是也是DataAccessException异常。
我们再进下一里面一层查看,直接去看他在接口的定义,发现他抛的是SQLException
查看的继承关系图:
这2个异常八竿子打不着,所以我们修改为抛父类的异常,这样在应用层面就能捕获到异常
修改完了之后查看一下报错信息:
不过我们一般不能直接写e.printStackTrace,好像是在阿里的Java规范手册里面说禁止这种写法。
改成这种会比较好
def updateBatch(sql: String, argList: List[Array[Any]]): Unit = {
FrequentChecker.sqlRecord(sql)
try {
jdbcTemplate.batchUpdate(sql, argList.map(_.map(_.asInstanceOf[AnyRef])).asJava)
} catch {
case e: Exception => logger.error(s"updateBatch error, sql: ${sql}, error message: ${e.getMessage}")
}
}
问题2:只有一行数据能更新成功
在问题1的时候已经告诉了我们答案
出现语法问题,说白了就是上一条sql语句有2个分号,最后的那个分号跑到下一条sql语句了,导致出现语法错误了。