這里面記錄一下使用mybatis處理mysql的批量插入的問題,測試有可能不准。只願世間風景千般萬般熙攘過后,字里行間,人我兩忘,相對無言。
mybatis的批量插入
我們的測試主體類是springboot環境中的一個控制器類,重要的代碼如下,在我們的測試中Constants.MAX_BATCH_NUMBER = 10000。
@GetMapping("insert") public void insertBatchData() { // 構建一個list,大小為1百萬條數據 long beginCreateList = System.currentTimeMillis(); List<Map<String, Object>> lists = new ArrayList<>(); for (int i = 0; i < 100000; i ++) { Map<String, Object> map = new HashMap<>(); map.put("userId", i + ""); map.put("username", "huhx" + i); map.put("password", "124" + i); map.put("sex", 1); map.put("address", System.currentTimeMillis()); lists.add(map); } long endCreateList = System.currentTimeMillis(); logger.debug("創建一個大小為10萬的列表,耗時:" + (endCreateList - beginCreateList)); // 4103 // 插入數據 dbSessionTemplateSupport.simpleSqlInsertBatch("user.simpleInsertUserData", lists); long endInsertData = System.currentTimeMillis(); logger.debug("插入10萬數據,耗時:" + (endInsertData - endCreateList)); // 49649 }
一、我們每10000條數據提交一次事務
public class DbSessionTemplateSupport extends SqlSessionTemplate { public DbSessionTemplateSupport(SqlSessionFactory sqlSessionFactory) { super(sqlSessionFactory); } // 支持批量的插入 public void baseInsertBatch(String sqlStatement, List<Map<String, Object>> list) { SqlSession session = getSqlSessionFactory().openSession(ExecutorType.BATCH, false); if (list == null || list.size() < 1) { return; } int listSize = list.size(); try { // 如果提交的列表條數小於提交閥值 if (listSize <= Constants.MAX_BATCH_NUMBER) { for (int i = 0; i < list.size(); i++) { session.insert(sqlStatement, list.get(i)); } session.commit(); } else { for (int i = 0; i < list.size(); ) { session.insert(sqlStatement, list.get(i)); i++; if (i % Constants.MAX_BATCH_NUMBER == 0 || i == listSize) { session.commit(); session.clearCache(); } } } } catch (Exception e) { session.rollback(); e.printStackTrace(); } finally { session.close(); } } }
這種方式處理插入,仍舊比較慢(其實是很慢很慢,可能是我的代碼問題,沒有統計時間,太慢了)。但是這種方式可以支持oracle,下面的這種方式非常快,但是oracle不支持。
二、采用mysql支持的拼接式插入數據
/** * mysql的批量插入方式,oracle不支持。 * * @param sqlStatement * @param list */ public void simpleSqlInsertBatch(String sqlStatement, List<Map<String, Object>> list) { if (list == null || list.size() < 1) { return; } // 如果提交的列表條數小於提交閥值 List<Map<String, Object>>[] splitLists = CommUtil.splitLists(list, Constants.MAX_BATCH_NUMBER); for (List<Map<String, Object>> tempList : splitLists) { insert(sqlStatement, tempList); } }
我們對原始的列表進行切割,然后依次的插入。每次的插入都是MAX_BATCH_NUMBER條數據。下面是切割的方法
/** * 對一個列表按照splitNum進行分割。 * * @param lists * @param splitNum * @param <T> * @return */ public static <T> List<T>[] splitLists(List<T> lists, int splitNum) { int listSize; if (lists == null || (listSize = lists.size()) < 1) { return new ArrayList[0]; } int length = listSize % splitNum == 0 ? listSize / splitNum : listSize / splitNum + 1; // 這里面如果用ArrayList,會在50行報錯。ArrayList list = new List();這樣會報錯。 List<T>[] results = new List[length]; int fromIndex, toIndex; for (int i = 0; i < length; i++) { fromIndex = i * splitNum; toIndex = (fromIndex + splitNum) > listSize ? listSize : (fromIndex + splitNum); results[i] = lists.subList(fromIndex, toIndex); } return results; }
插入的sql語句在mybatis中是使用for...each的方式,如下:
<!-- mysql的批量插入方式 --> <insert id="simpleInsertUserData" parameterType="java.util.List"> INSERT INTO puser (userId, username, password, address, sex) VALUES <foreach collection ="list" item="item" index= "index" separator =","> ( #{item.userId}, #{item.username}, #{item.password}, #{item.address}, #{item.sex} ) </foreach > </insert>
10萬條數據的分割時間加上插入到mysql數據庫,這種方式耗時:15658毫秒。需要注意的是如果常數設置為10萬條,也就是第10萬插入一次。這種方式會報錯的。
友情鏈接