那些年咱們爬過的山 - mybatis批量導入

【原創做品,轉載請註明出處】html

寫這篇文章以前想着給這篇博客起一個文藝一點的標題,思來想去,想到了那些年咱們爬過的山,或者咱們一塊兒趟過的河?代碼不規範,同事兩行淚,這是多麼痛的領悟啊!java

背景

本組一名實習生,因爲學校有事情須要回去處理,我便將他的代碼接管過來,正好遇上本次迭代上線,須要將同事的代碼提交測試,若是被測試出來有bug,我就來負責bug修復,代碼優化等。因爲不一樣的開發人員都有本身的編程習慣,因此不一樣人員所寫的代碼多少都會有些差別,好比:變量的命名、代碼的格式等,即便組內有一套開發規範也仍是會出現一些差別,這就是所謂的個性?特色? 固然 特色只是特色,並不能稱得上個性。既然風格不一樣,那麼看別人代碼的時候會有值得學習的地方,也有本身感受不舒服的地方。也罷,畢竟人都有「個性」。mysql

問題現象

此次我接手同事的代碼主要是一個Excel導入數據庫這樣一個功能,在測試人員測試導入數據的時候,使用的導入數據有2w條做爲測試數據導入數據庫,現象是 導入後發現頁面先是顯示上傳中,以後頁面沒有任何反饋,經排查後發現是由於後臺還在處理導入數據邏輯,時間過長頁面沒有獲得反饋,致使頁面超時。sql

排查過程

  • 接着查看了導入邏輯,發現導入的時候,使用的是將2w條數據循環導入數據庫,每一次導入都會進行數據庫交互,致使數據庫鏈接池的鏈接被耗盡,會再次進行建立、分配、釋放等操做,從而致使以後的處理變得緩慢。
  • 基於這些考慮使用了批量導入,批量導入的目的就是一次導入多條數據,減小和數據庫交互次數,減輕數據庫壓力。
  • 修改程序後測試導入所用的時間並不理想,繼續排查發現 在導入前還有判斷卡密是否存在數據庫 這樣一個操做,和循環插入沒有什麼區別,也是會循環2w次數據庫查詢。所以首先想到的是減小和數據庫的交互次數,先把數據查詢出來,放到內存中,再將兩個集合進行比較,發現數據量小的時候還能夠,數據量大了就會很緩慢,嘗試了幾個網上搜到的「高效」list去重,發現效果並不明顯。
  • 遂繼續找其餘的方法,最後找到使用MySQL的ignore關鍵字【做用:如有致使unique key 衝突的記錄,則該條記錄不會被插入到數據庫中,去重字段必定要是惟一索引】就能夠解決判斷卡密重複的問題,** 通過測試 導入2w條數據由原來的16分鐘,減小到目前的10秒 左右** 。

總結

對於本次問題的排查,能夠總結爲問題發現,和問題排查。發現問題時首先要快速瞭解該功能的主要邏輯是什麼,此次的導入主要有兩點,一是 導入前判斷是否有和數據庫重複,二是導入操做。弄明白主要邏輯以後,就要分析,導入慢確定是這兩個邏輯的某一個邏輯慢,或者是兩個邏輯都慢。通過分析發現,這兩個邏輯都出現了循環創建數據庫鏈接的問題,發現了問題的根本緣由後,就要減小創建數據庫鏈接次數,問題便獲得解決。數據庫

主要代碼

對於導入前和數據庫判斷是否有重複的,使用了mysql的一個關鍵字ignore,關鍵字的做用是:如有致使unique key 衝突的記錄,則該條記錄不會被插入到數據庫中,去重字段必定要是惟一索引。其餘就是拼接SQL使用批量導入。下面將主要的代碼貼出來供你們參考。編程

  • 批量導入時每次批量插入100條數據,這樣數據庫鏈接創建的次數就從原來的 20000次減小到200次。
// 批量入庫,每次批量插入100條
List<Detail> detailList = new ArrayList<>(); 
List<List<Detail>> tempList = new ArrayList<>();
    int insertCount = 100;
    for (int i = 0; i < detailList.size(); i += insertCount) {
        if ((i + insertCount) < detailList.size()) {
            List<Detail> newList = new ArrayList<>();
            newList.addAll(detailList.subList(i, i + insertCount));
            tempList.add(newList);
        } else {
            List<Detail> newList = new ArrayList<>();
            newList.addAll(detailList.subList(i, detailList.size()));
            tempList.add(newList);
        }
    }
Map<String, Object> map = detailManager.batchInsertDetail(tempList);
  • mybatis的寫法
<insert id="batchInsertDetail" parameterType="java.util.List">
	  insert ignore into my_table
		(id,code,status)
	  values
		<foreach collection ="list" item="detail" index= "index" separator =",">
			(#{detail.id,jdbcType=BIGINT},#{detail.code,jdbcType=VARCHAR},#{detail.status,jdbcType=TINYINT})
		</foreach >
</insert>

原文出處:https://www.cnblogs.com/luao/p/10503345.htmlmybatis

相關文章
相關標籤/搜索