相信有过较大数据测试或者大数据处理经验的人对这个配置不陌生,为什么呢,我来给你们解释解释吧。
这一篇文章写的较为中性,所以有不足的可以提出,我可以精进修改,给需要的人了解哈!
首先,我们在处理许多业务的时候,会需要对数据的批量操作的CRUD,但是捏,问题就来了。
如何实现批量的添加,更改和删除呢?
1.for循环
2.session批量插入
3.foreach插入方式
以上三种方式呢,不管你用的哪一个,嘿嘿,只要你没有设置rewriteBatchedStatement这个参数,他都会给你一条一条的发送出去,那速度也不用说了。因为mysql的默认配置是为false。
下面我来来贴一段源代码(以更新来测试的),最后是执行到了executeBatchInternal方法:
protected long[] executeBatchInternal() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.connection.isReadOnly()) {
throw new SQLException(Messages.getString("PreparedStatement.25") + Messages.getString("PreparedStatement.26"),
SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
}
if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
return new long[0];
}
// we timeout the entire batch, not individual statements
int batchTimeout = this.timeoutInMillis;
this.timeoutInMillis = 0;
resetCancelledState();
try {
statementBegins();
clearWarnings();
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);
} finally {
this.statementExecuting.set(false);
clearBatch();
}
}
}
你用的batchUpdate最终执行就是到上面的源码,然后return的是exexuteBatchSerially,官方给他的解释是什么呢.
Executes the current batch of statements by executing them one-by-one.
然后我们进入到 exexuteBatchSerially这个方法里看一看
for (this.batchCommandIndex = 0; this.batchCommandIndex < nbrCommands; this.batchCommandIndex++) {
Object arg = this.batchedArgs.get(this.batchCommandIndex);
try {
if (arg instanceof String) {
updateCounts[this.batchCommandIndex] = executeUpdateInternal((String) arg, true, this.retrieveGeneratedKeys);
// limit one generated key per OnDuplicateKey statement
getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString((String) arg) ? 1 : 0);
} else {
BatchParams paramArg = (BatchParams) arg;
//核心代码,for循环执行每一条SQL
updateCounts[this.batchCommandIndex] = executeUpdateInternal(paramArg.parameterStrings, paramArg.parameterStreams,
paramArg.isStream, paramArg.streamLengths, paramArg.isNull, true);
// limit one generated key per OnDuplicateKey statement
getBatchedGeneratedKeys(containsOnDuplicateKeyUpdateInSQL() ? 1 : 0);
}
} catch (SQLException ex) {
updateCounts[this.batchCommandIndex] = EXECUTE_FAILED;
if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException)
&& !hasDeadlockOrTimeoutRolledBackTx(ex)) {
sqlEx = ex;
} else {
long[] newUpdateCounts = new long[this.batchCommandIndex];
System.arraycopy(updateCounts, 0, newUpdateCounts, 0, this.batchCommandIndex);
throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor());
}
}
}
上面我标明的位置,可以看到源码上写的也是,sql是一条一条的传送到服务器的,而不是batch的形式。
然后我们回到一开始的executeBatchInternal这个类里看一看,想要进入到batch是有条件的
image.pngbatchHasPlainStatements是默认false的,然后决定性的就是后面的条件this.connection.getRewriteBatchedStatements()这个开关,这个开关也就是我们在配置的时候rewriteBatchedStatement这个参数是否配置为true。如果为true的情况下,它就会重新给你通过切割重组(源码就是这么个样)然后拼成一串比较长而且骚的sql,哈哈哈哈,然后发送给服务器.(但是官方默认为false也是有原因的,因为怕有些语句没有办法进行重组,导致一些小问题,大家都知道这种东西很*蛋。)
结尾再给你们补一下小知识点
这个是什么呢,就是它的批量的数量是有要求的,如果没有达到要求,它还是一条一条走的,就在刚才的源码上就能看到了
image.png
看了这里都知道啦,基本讲完。
网友评论