批量插入的一万种场景之二
在日常工作中,我们经常会遇到各种各样的需求,需要将数据插入到数据库中,不过不同的需求用不同的插入写法,这样能提升很大的效率,这篇文章会根据不同的场景使用不同的批量插入代码,大家以后遇到批量插入直接来我这里粘贴就可以了。
场景一:
app中最常见的pv和uv接口,用户每点击一次都需要在数据库中插入一条数据。这种情况我们不可能一直请求数据库,肯定会将数据先存到缓存或者队列中,然后批量插入到数据库。
上队列代码
public void run() { startLog(); while (!isInterrupted()) { List<T> list = new ArrayList<T>(); preProcessor(); try { long ts = 0; //每一百条批量插入一次 for (int i = 0; i < 100; ) { T tmp = null; try { tmp = queue.poll(5000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } if (tmp != null) { //记录第一次添加数据后的时间 if (i++ == 0) { ts = System.currentTimeMillis(); } //处理 processor(list, tmp); continue; } //有数据 且从第一次插入数据已经过了 设定时间 则 执行插入 if (list.size() > 0 && System.currentTimeMillis() - ts >= BATCH_TIMEOUT) { break; } } save(list); } catch (Exception e) { e.printStackTrace(); } } }
上mybatis代码
<insert id="batchInsert" parameterType="List"> insert into dsc_user ( user_name, nick_name, mobile ) values <foreach collection='list' item='item' index='index' separator=','> ( #{item.userName,jdbcType=VARCHAR},#{item.nickName,jdbcType=VARCHAR},#{item.mobile,jdbcType=VARCHAR} ) </foreach> </insert>
注:这个地方不能把foreach标签写到外层,否则还是会不停的创建关闭sqlsession
场景二:
某个功能导入数据总是千万级别,如何最快的将数据插入到数据库中
这种大数据量导入数据 和 小数据量导入是有很大区别的,同样的代码,小数据量导入可以完美实现,但数据量大了之后就会出现各种各样的问题
首先,我们要重视的是内存,如果要实现千万级别的数据导入,JVM的堆内存一定要设置一个比较大的数值
其次,要分段保存到数据库,比如每1000条数据执行一次
最后,不要使用mybatis,因为myabtis封装程度太高,解析SQL模板和SQL拼接会消耗大量时间,要使用原生的JDBC
上代码
public void insertBatch() { Connection conn = null; Statement statement = null; try { conn = DriverManager.getConnection(url, username, password); conn.setAutoCommit(false); statement = conn.createStatement(); for (int i = 0; i < 10000000; i++) { String sql = "insert into dsc_user(user_name, nick_name, mobile) values ('"+i+"', '第"+i+"条数据', '158'+"+ i +")"; //利用addBatch方法将SQL语句加入到stmt对象中 statement.addBatch(sql); if (i % 1000 == 0 && i != 0) { //利用executeBatch方法执行1000条SQL语句 statement.executeBatch(); statement.clearBatch(); conn.commit(); } } statement.executeBatch(); statement.clearBatch(); conn.commit(); close(); //关闭资源 } catch (SQLException e) { e.printStackTrace(); } }
一般情况下,我们很少遇到需要批量插入千万级别数据的,所以批量插入只需要在mybatis中使用一下foreach标签就能解决,如下代码
public void insertBatch(List<CouponHistory> coupons){ if (coupons == null || coupons.isEmpty()){ return; } int index = 1000; int currentPageStart = 0; int currentPageEnd = 0; while (currentPageEnd < coupons.size()){ currentPageStart = currentPageEnd; currentPageEnd = Math.min(currentPageEnd + index, coupons.size()); //调用mybatis批量插入 batchInsert(coupons.subList(currentPageStart, currentPageEnd)); } }
<insert id="batchInsert" parameterType="List"> insert into dsc_user ( user_name, nick_name, mobile ) values <foreach collection='list' item='item' index='index' separator=','> ( #{item.userName,jdbcType=VARCHAR},#{item.nickName,jdbcType=VARCHAR},#{item.mobile,jdbcType=VARCHAR} ) </foreach> </insert>
大家在做批量插入时只需要将最后这块代码复制过去就可以啦
注:mybatis-plus中批量插入方法就是 for循环单条插入;
以上就是批量导入两个场景中的解决方案,今天就写到这里,再见啦!
昔我往矣,杨柳依依
今我来思,雨雪霏霏
【白话译文】
回想当初出征时,杨柳依依随风吹;如今回来路途中,大雪纷纷满天飞