分布式数据库插入速度很慢
Distributed database insertion speed is very slow
@Test
public void transaction() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
String sql = "insert into `1` values(?, ?, ?, ?)";
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
conn.setAutoCommit(false);
for(int i = 1; i <= 10000; i++){
ps.setObject(1, i);
ps.setObject(2, 10.12345678);
ps.setObject(3, "num_" + i);
ps.setObject(4, "2021-12-24 19:00:00");
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
conn.commit();
} catch (Exception e) {
conn.rollback();
e.printStackTrace();
}finally {
JDBCUtils.closeResources(conn, ps);
}
}
当 setAutoCommit = true 时,本地 MySQL 和分布式 MySQL 插入速度非常慢。
当我将事务设置为手动提交时,就像上面的代码一样,本地MySQL速度提高了很多,但是分布式MySQL的插入速度仍然很慢。
我还需要设置其他参数吗?
设置参数可能无济于事(很多)。
速度慢有几个原因:
在 autocommit=true
中,您将提交每个插入语句。这意味着每个新行都必须在数据库服务器 returns 响应客户端之前写入磁盘。
对于 autocommit=false
,每个 insert
语句仍然存在客户端 -> 服务器 -> 客户端往返。这些往返行程加起来会花费大量时间。
加快速度的一种方法是在每个插入语句中插入多行,但这很麻烦,因为您需要生成复杂的(多行)插入语句。
更好的方法是使用 JDBC 的批处理功能来减少往返次数。例如:
PreparedStatement ps = c.prepareStatement("INSERT INTO employees VALUES (?, ?)");
ps.setString(1, "John");
ps.setString(2,"Doe");
ps.addBatch();
ps.clearParameters();
ps.setString(1, "Dave");
ps.setString(2,"Smith");
ps.addBatch();
ps.clearParameters();
int[] results = ps.executeBatch();
(归因:以上代码由@Tusc 从this answer 复制而来)
如果仍然不够快,您应该使用 MySQL 的本地批量插入机制获得更好的性能;例如load data infile
;见 High-speed inserts with MySQL
为了完整起见,我添加了@Wilson Hauck 的建议
"In your configuration [mysqld] section, innodb_change_buffer_max_size=50 # from 25 (percent) for improved INSERT rate per second. SHOW FULL PROCESSLIST; to monitor when the instance has completed adjustment, then do your inserts and put it back to 25 percent for typical processing speed."
这可能会增加插入率,具体取决于您的 table 及其索引,以及您插入行的顺序。
但不利的一面是,您可以通过其他方式实现相同的加速(或更多!);例如
- 通过对输入进行排序,以便按索引顺序插入行,或者
- 删除索引,插入记录,然后重新创建索引。
您可以阅读更改缓冲区 here 并做出自己的判断。
@Test
public void transaction() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
String sql = "insert into `1` values(?, ?, ?, ?)";
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
conn.setAutoCommit(false);
for(int i = 1; i <= 10000; i++){
ps.setObject(1, i);
ps.setObject(2, 10.12345678);
ps.setObject(3, "num_" + i);
ps.setObject(4, "2021-12-24 19:00:00");
ps.addBatch();
}
ps.executeBatch();
ps.clearBatch();
conn.commit();
} catch (Exception e) {
conn.rollback();
e.printStackTrace();
}finally {
JDBCUtils.closeResources(conn, ps);
}
}
当 setAutoCommit = true 时,本地 MySQL 和分布式 MySQL 插入速度非常慢。
当我将事务设置为手动提交时,就像上面的代码一样,本地MySQL速度提高了很多,但是分布式MySQL的插入速度仍然很慢。
我还需要设置其他参数吗?
设置参数可能无济于事(很多)。
速度慢有几个原因:
在
autocommit=true
中,您将提交每个插入语句。这意味着每个新行都必须在数据库服务器 returns 响应客户端之前写入磁盘。对于
autocommit=false
,每个insert
语句仍然存在客户端 -> 服务器 -> 客户端往返。这些往返行程加起来会花费大量时间。
加快速度的一种方法是在每个插入语句中插入多行,但这很麻烦,因为您需要生成复杂的(多行)插入语句。
更好的方法是使用 JDBC 的批处理功能来减少往返次数。例如:
PreparedStatement ps = c.prepareStatement("INSERT INTO employees VALUES (?, ?)");
ps.setString(1, "John");
ps.setString(2,"Doe");
ps.addBatch();
ps.clearParameters();
ps.setString(1, "Dave");
ps.setString(2,"Smith");
ps.addBatch();
ps.clearParameters();
int[] results = ps.executeBatch();
(归因:以上代码由@Tusc 从this answer 复制而来)
如果仍然不够快,您应该使用 MySQL 的本地批量插入机制获得更好的性能;例如load data infile
;见 High-speed inserts with MySQL
为了完整起见,我添加了@Wilson Hauck 的建议
"In your configuration [mysqld] section, innodb_change_buffer_max_size=50 # from 25 (percent) for improved INSERT rate per second. SHOW FULL PROCESSLIST; to monitor when the instance has completed adjustment, then do your inserts and put it back to 25 percent for typical processing speed."
这可能会增加插入率,具体取决于您的 table 及其索引,以及您插入行的顺序。
但不利的一面是,您可以通过其他方式实现相同的加速(或更多!);例如
- 通过对输入进行排序,以便按索引顺序插入行,或者
- 删除索引,插入记录,然后重新创建索引。
您可以阅读更改缓冲区 here 并做出自己的判断。