转载

聊聊java里mysql的batch操作

众所周知,对于mysql,使用批量操作,可以大幅度提升大数据量下操作的性能。不过,在java中使用mysql时,有些细节务必注意,否则就享受不到批量操作的性能了。

前阵子,发现一条sql的性能明细不正常,几千条数据批量insert,耗时居然到了秒级。排除了服务器性能、网络的问题后,开始怀疑是代码的问题,导致没有真正进行批量操作,感觉只有一条一条去跟数据库交互的话才会出现这种性能。

调了一下代码,翻到了mysql-connector里,关键的代码是这么一块:

if (!this.batchHasPlainStatements && this.connection.getRewriteBatchedStatements()) {

                    if (canRewriteAsMultiValueInsertAtSqlLevel()) {
                        return executeBatchedInserts(batchTimeout);
                    }

                    if (this.connection.versionMeetsMinimum(4, 1, 0) && !this.batchHasPlainStatements && this.batchedArgs != null
                            && this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
                        return executePreparedBatchAsMultiStatement(batchTimeout);
                    }
                }

                return executeBatchSerially(batchTimeout);

可以看到有executeBatchedInserts和executeBatchSerially两个分支,如果执行到executeBatchSerially的话,其实就是执行了个“假的”batch, 在这个方法里会每条数据单独去跟mysql交互。

为了能顺利的执行进executeBatchedInserts, 可以看到有一些先决条件:batchHasPlainStatements, connection.getRewriteBatchedStatements(), canRewriteAsMultiValueInsertAtSqlLevel。

事实上,为了能顺利的执行批量sql, mysql的驱动会对sql做一次“改写(rewrite)”,这个改写是什么意思呢?其实很简单,比如本来有一堆sql:

insert into () values()
insert into () values()
...
insert into () values()

改写完之后,变成了这样:

insert into () values () () ()

变成了一条sql, 当然就是与mysql交互一次了。

成功rewrite后,才能批量执行sql。上边的几个条件也基本都是为了能让sql进行改写。这里边关键的其实就是两个点,一是数据库连接层面,需要配置rewriteBatchedStatements=true, 允许对sql进行改写。第二是sql层面,得要求这个sql能被改写。我们知道,mysql有这样一种语法:

insert into () set x=x,y=y

这种语法本身不属于sql标准,是mysql的“方言”,也是没有批量操作的形式的。因此,如果把sql写成这种形式,就不然没法执行真正的批量操作了。

原文地址: http://lichuanyang.top/posts/63688/

原文  https://lichuanyang.top/posts/63688/
正文到此结束
Loading...