測試環境
- 10 萬條
User(id, username, password)
- Spring + Hibernate 4 + JdbcTemplate
- 事務邊界:JDBC 手動控制、JdbcTemplate/Hibernate
@Transactional
三種實現核心代碼
1. 原生 JDBC + Batch
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareStatement("INSERT INTO t_user(username,password) VALUES(?,?)");
for (User u : users) {
ps.setString(1, u.getUsername());
ps.setString(2, u.getPassword());
ps.addBatch();
}
ps.executeBatch();
conn.commit();
2. JdbcTemplate.batchUpdate
String sql = "INSERT INTO t_user(username,password) VALUES(?,?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) {
ps.setString(1, users.get(i).getUsername());
ps.setString(2, users.get(i).getPassword());
}
public int getBatchSize() { return users.size(); }
});
3. Hibernate Session.save + 手動 flush/clear
Session session = sessionFactory.getCurrentSession();
for (int i = 0; i < users.size(); i++) {
session.save(users.get(i));
if (i % 20 == 0) { // 批次提交
session.flush();
session.clear();
}
}
實測結果(10 萬條)
| 方案 | 耗時 | 相對性能 | 備註 |
| JDBC Batch | ~15 秒 | 1.0x (基準) | 手動事務控制 |
| JdbcTemplate | ~15.6 秒 | 0.96x | 自動事務、代碼簡潔 |
| Hibernate | ~131 秒 | 0.11x | ~9 倍慢,大量對象管理開銷 |
深度分析
- JDBC/JdbcTemplate 直發
PreparedStatement.addBatch,網絡層合併,開銷極低
- Hibernate:一級緩存、脏檢查、實體狀態管理、主鍵生成器交互、SQL 解析等開銷大
- Hibernate 優化鍵:
hibernate.jdbc.batch_size、hibernate.order_inserts=true、hibernate.order_updates=true、IDENTITY 主鍵改 SEQUENCE/TABLE、StatelessSession
生產選型建議
- 極致性能、大批量:原生 JDBC Batch / JdbcTemplate
- 業務邏輯複雜、需 ORM 便利:Hibernate + 批次 flush/clear +
StatelessSession
- 中間路線:MyBatis
ExecutorType.BATCH / JdbcTemplate
- 通用優化:
rewriteBatchedStatements=true (MySQL Connector/J 參數,將多條 INSERT 重寫為多值單條)
關鍵連接參數
jdbc:mysql://host:3306/db?rewriteBatchedStatements=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048