Why MyBatis‑Plus saveBatch Is Slow and How to Speed It Up 30×
This article analyzes the poor performance of MyBatis‑Plus saveBatch, compares it with single inserts, manual SQL batching, and JDBC executeBatch, and shows how enabling the rewriteBatchedStatements flag can dramatically improve batch insert speed.
Hello, I am Su San. While load‑testing a set of APIs I discovered that batch saving to the database was much slower than expected.
The project uses
mybatis-plusand its
saveBatchmethod. Looking at the source code, the implementation loops over each record and calls
sqlSession.insertfor every item, then flushes after a certain count.
1000 rows, inserted one by one
@Test
void MybatisPlusSaveOne() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
StopWatch stopWatch = new StopWatch();
stopWatch.start("mybatis plus save one");
for (int i = 0; i < 1000; i++) {
OpenTest openTest = new OpenTest();
openTest.setA("a" + i);
openTest.setB("b" + i);
openTest.setC("c" + i);
openTest.setD("d" + i);
openTest.setE("e" + i);
openTest.setF("f" + i);
openTest.setG("g" + i);
openTest.setH("h" + i);
openTest.setI("i" + i);
openTest.setJ("j" + i);
openTest.setK("k" + i);
// one‑by‑one insert
openTestService.save(openTest);
}
sqlSession.commit();
stopWatch.stop();
log.info("mybatis plus save one:" + stopWatch.getTotalTimeMillis());
} finally {
sqlSession.close();
}
}The execution time for inserting 1000 rows one by one was 121,011 ms .
1000 rows inserted with MyBatis‑Plus saveBatch
@Test
void MybatisPlusSaveBatch() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
List<OpenTest> openTestList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
OpenTest openTest = new OpenTest();
openTest.setA("a" + i);
openTest.setB("b" + i);
openTest.setC("c" + i);
openTest.setD("d" + i);
openTest.setE("e" + i);
openTest.setF("f" + i);
openTest.setG("g" + i);
openTest.setH("h" + i);
openTest.setI("i" + i);
openTest.setJ("j" + i);
openTest.setK("k" + i);
openTestList.add(openTest);
}
StopWatch stopWatch = new StopWatch();
stopWatch.start("mybatis plus save batch");
// batch insert
openTestService.saveBatch(openTestList);
sqlSession.commit();
stopWatch.stop();
log.info("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());
} finally {
sqlSession.close();
}
}Using
saveBatchreduced the time to 59,927 ms , roughly twice as fast as the single‑insert loop.
Next, I tried a manual SQL‑concatenation approach.
1000 rows inserted with manually concatenated SQL
@Test
void MapperSaveBatch() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
List<OpenTest> openTestList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
OpenTest openTest = new OpenTest();
openTest.setA("a" + i);
openTest.setB("b" + i);
openTest.setC("c" + i);
openTest.setD("d" + i);
openTest.setE("e" + i);
openTest.setF("f" + i);
openTest.setG("g" + i);
openTest.setH("h" + i);
openTest.setI("i" + i);
openTest.setJ("j" + i);
openTest.setK("k" + i);
openTestList.add(openTest);
}
StopWatch stopWatch = new StopWatch();
stopWatch.start("mapper save batch");
// manual batch insert
openTestMapper.saveBatch(openTestList);
sqlSession.commit();
stopWatch.stop();
log.info("mapper save batch:" + stopWatch.getTotalTimeMillis());
} finally {
sqlSession.close();
}
}This method finished in 2,275 ms**, a 26‑fold improvement over MyBatis‑Plus saveBatch.
I also benchmarked plain JDBC
executeBatch:
1000 rows inserted with JDBC executeBatch
@Test
void JDBCSaveBatch() throws SQLException {
SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection();
connection.setAutoCommit(false);
String sql = "insert into open_test(a,b,c,d,e,f,g,h,i,j,k) values(?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatement statement = connection.prepareStatement(sql);
try {
for (int i = 0; i < 1000; i++) {
statement.setString(1, "a" + i);
statement.setString(2, "b" + i);
statement.setString(3, "c" + i);
statement.setString(4, "d" + i);
statement.setString(5, "e" + i);
statement.setString(6, "f" + i);
statement.setString(7, "g" + i);
statement.setString(8, "h" + i);
statement.setString(9, "i" + i);
statement.setString(10, "j" + i);
statement.setString(11, "k" + i);
statement.addBatch();
}
StopWatch stopWatch = new StopWatch();
stopWatch.start("JDBC save batch");
statement.executeBatch();
connection.commit();
stopWatch.stop();
log.info("JDBC save batch:" + stopWatch.getTotalTimeMillis());
} finally {
statement.close();
sqlSession.close();
}
}The JDBC batch took 55,663 ms**, similar to MyBatis‑Plus saveBatch because both ultimately use the same driver logic.
Investigating the MySQL driver revealed the
rewriteBatchedStatementsflag. When set to
true, the driver rewrites a batch of inserts into a single statement like
INSERT INTO xxx VALUES (a),(b),(c)…, achieving the same effect as manual concatenation.
By adding
rewriteBatchedStatements=trueto the JDBC URL, the performance improved dramatically:
mybatis‑plus
saveBatchwith the flag: 2,589 ms
JDBC
executeBatchwith the flag: 324 ms
Thus, for any batch‑insert requirement, enabling
rewriteBatchedStatementsyields a huge speedup, and manual SQL concatenation should be used with careful batch sizing.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.