1. 前言
最近系统中增加了一个下发数据的功能,由于系统用户量较大,这个需要最后要插入数据库大概5万多条数据,虽然算不上大数据,但是也需要好好考虑性能问题了。
开始用mybatis的批量插入做了一版,整体上分批插入,每批插入3000条(在生产环境试过一次5000条,结果卡了改成对半砍),mapper代码大概如下图,只要程序中计算得到所有列表数据传入insertBatch方法中即可(加了springBoot自带事务)
情理之外,意料之中,前端发了请求后超过300s没有返回response报错了
2. 多线程方案
这个是从网上抄的方案,直接运行发现确实快了,但是想想人家多线程都是解决百万级别的数据,我这个需求用多线程有点小题大做了,况且生产环境万一发生什么并发类的问题我们这里不太容易定位,因为接触不到内网数据,需要出差到现场,有没有更简洁的方式呢?
因为没用多线程,抄的代码就不贴了
3. PreparedStatement批量插入
基于以前的经验,查询数据量较大时用jdbc直接连数据库比用mybatis的mapper文件快很多
,所以就改成用jdbc查数据库,不可避免用到PreparedStatement.executeBatch()方法,这里也有个坑,一定要记得手动管理事务哦!
最终批量插入代码:
private static final int THREAD_COUNT_SIZE = 3000;
public void batchInsert(List<Product> productList) {
long start = System.currentTimeMillis();
// 分批数
int round = productList.size() / THREAD_COUNT_SIZE + 1;
for (int i = 0; i < round; i++) {
int startLen = i * THREAD_COUNT_SIZE;
int endLen = ((i + 1) * THREAD_COUNT_SIZE > productList.size() ? productList.size() : (i + 1) * THREAD_COUNT_SIZE);
final List<Product> threadList = productList.subList(startLen, endLen);
Connection conn = JdbcUtil.getConnection();
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("INSERT INTO PRODUCT (ID,PRODUCTNAME,PRODUCTCODE,PRODUCTDESC,PARENTID,")
.append("SORT,LEVEL,ORGID,ORGCODE,ORGNAME,ORGPATH,CREATEBY,CREATEUSERID,CREATETIME)")
.append("VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
PreparedStatement preparedStatement = null;
try {
conn.setAutoCommit(false);
preparedStatement = conn.prepareStatement(sqlBuilder.toString());
//循环
for (int k = 0; k < threadList.size(); k++) {
ProductModel u = threadList.get(k);
//使用参数对象替换预编SQL语句中的占位符
preparedStatement.setString(1, u.getId());
preparedStatement.setString(2, u.getProductname());
preparedStatement.setString(3, u.getProductcode());
preparedStatement.setString(4, u.getProductdesc());
preparedStatement.setString(5, u.getParentid());
preparedStatement.setString(6, u.getProductmodelpath());
preparedStatement.setString(7, u.getSort());
preparedStatement.setString(8, u.getLevel());
preparedStatement.setString(9, u.getOrgid());
preparedStatement.setString(10, u.getOrgcode());
preparedStatement.setString(11, u.getOrgname());
preparedStatement.setString(12, u.getOrgpath());
preparedStatement.setString(13, u.getCreateBy());
preparedStatement.setString(14, u.getCreateUserId());
preparedStatement.setDate(15, new java.sql.Date(System.currentTimeMillis()));
//循环一次就会生成一条sql语句
preparedStatement.addBatch();
}
preparedStatement.executeBatch();
conn.commit();
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println("批量插入耗时:" + (end - start) + "ms");
}
测试结果,插入50250条数据,耗时16279ms, 这个结果还可以接受。
做个有追求的程序员,朋友们有更好的方式可以讨论下!