使用 5.xx.xx 以上版本的 MySQL Connector/J

工欲善其事,必先利其器。只有使用 5.xx.xx 以上版本的 MySQL Connector/J 才能增删改的性能提高。虽然互联网上有人说 5.0.7 以下版本也无法提升性能,但是我自己测试了一下,所有 5.xx.xx 以上版本的 MySQL Connector/J 都能不同程度地提高性能。

由于 maven 中央仓库没有 4.xx.xx 版本的,所以我测试了一下 3.xx.xx 版本的,发现确实没法提升性能。

设置 rewriteBatchedStatements

这里,我的 MySQL Connector/J 使用的时5.1.47版本的,为了使批处理的性能提高,我们需要将 rewriteBatchedStatements 设置为 true,如下所示:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_test?rewriteBatchedStatements=true", "root", "");

为什么需要这样设置呢?通过使用 IDEA 对代码进行追踪,我发现真正执行 executeBatch() 的是executeBatchInternal() 方法,其源码如下:

@Override
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();
				}
		}
}

可以看到这段代码中间有这样一段代码:

if (!this.batchHasPlainStatements && this.connection.getRewriteBatchedStatements()) {
    ...
}
return executeBatchSerially(batchTimeout);

如果没能将 rewriteBatchedStatements 设置为 true,就进入不到 if 里面,就会直接执行 executeBatchSerially()方法。该方法一次只能执行一条 SQL 语句。而 if 里的 executeBatchedInserts() 会先接收所有的 SQL 语句,然后将它们改写为多值插入。

正确的SQL

有个朋友写了这样一段代码:

PreparedStatement pstat = conn.prepareStatement("insert into user(username, password) value (?, ?)");

他一直各种调试也没能让插入的性能高起来。你发现问题了么?

原来他的“value”少了一个“s”。虽然这在 MySQL 中是正确,但是 MySQL Connector/J 却无法将它改写为一条正确的多值插入语句。毕竟“value”在 SQL Server 中是不合法的。

最后修改于 2019-04-11 17:35:46
上一篇