1、分析
參考:https://huaweicloud.csdn.net/63355e78d3efff3090b54618.html
(1)MySQL數(shù)據(jù)庫
針對MySQL數(shù)據(jù)庫saveBatch批量插入效率比較低登下,是比較好解決的茫孔,一般都是由于數(shù)據(jù)庫連接url上沒有配置批量操作的屬性叮喳,只需要在url上加上如下屬性即可:
rewriteBatchedStatements=true
即類似如下:
jdbc:mysql://數(shù)據(jù)庫地址/數(shù)據(jù)庫名?useUnicode=true&characterEncoding=UTF8&allowMultiQueries=true&rewriteBatchedStatements=true
加上之后,你就會發(fā)現(xiàn)缰贝,saveBatch的速度直線提升馍悟,效果還是很不錯的,一萬條數(shù)據(jù)估計也就在幾百毫秒剩晴。
(2)Oracle數(shù)據(jù)庫
Oracle數(shù)據(jù)庫的問題就比較大了锣咒,而且至今潘老師也沒找到一個比較完美的解決方案,此次寫這篇博客也正是由于Oracle數(shù)據(jù)庫saveBatch效率賊低引起的李破,先看下圖宠哄,批量插入一萬條數(shù)據(jù)(MyBatis-Plus的saveBatch默認(rèn)一次1000條,1w條會分10次嗤攻,當(dāng)然你也可以設(shè)置Batch Size)毛嫉,耗時竟然達到10s多,簡直不能忍啊妇菱,堪比龜速承粤!
于是就開始各種排查,經(jīng)過一番仔細debug闯团、翻源碼辛臊,反復(fù)測試對比驗證,最終發(fā)現(xiàn)是因為MyBatis-Plus的針對Oracle主鍵序列生成策略導(dǎo)致的房交,在上圖打印的日志可以看出彻舰,在每次打印參數(shù)之前,都會先執(zhí)行下SELECT XXX.NEXTVAL FROM DUAL候味,這是我們在實體Entity上加上了MyBatis-Plus的注解@KeySequence(value = “XXX”)引起的刃唤,本來預(yù)想的1萬條只需要和數(shù)據(jù)庫交互10次就解決了,現(xiàn)在看打印日志情況白群,預(yù)估是insert和查詢序列合計預(yù)計2萬次交互尚胞,于是,測試了下去掉@KeySequence注解帜慢,手工給id賦好值笼裳,再次批量保存1萬條,結(jié)果如下:
好家伙粱玲,直接干到1s多點躬柬,整整節(jié)約了10倍的時間,病根終于確定了抽减,就是@KeySequence注解導(dǎo)致的允青。
但你這就想把鍋甩給MyBatis-Plus那就大錯特錯了,這鍋歸根到底其實還是MyBatis的胯甩,為什么呢昧廷?經(jīng)過潘老師一番深入探查,發(fā)現(xiàn)正如MyBatis-Plus官方所說偎箫,只對MyBatis做擴展卻不改變木柬,做一對快樂的好基友,@KeySequence(value = “XXX”)作用就是結(jié)合KeyGenerator來實現(xiàn)自動化生成主鍵并回填淹办,但是MyBatis-Plus的所有KeyGenerator也是從MyBatis繼承擴展而來眉枕,而使用了之后,對于Oracle而言就相當(dāng)于在insert語句前加上了selectKey語句怜森,類似如下:
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="BEFORE">
select XXX.nextval from dual
</selectKey>
這是不是很熟悉速挑,就是我們學(xué)MyBatis時候?qū)W的啊,MyBatis-Plus只不過是把它變成了@KeySequence注解副硅,省去了你寫這段xml了而已姥宝,而所有問題的源頭就來自于這段xml,潘老師親自測試恐疲,在insert前加上這段xml后使用Mybatis原生的SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH)批量插入腊满,發(fā)現(xiàn)批量插入無效,耗時和MyBatis-plus差不多培己,1w條大概10多秒碳蛋,但是一旦去掉這段xml,再使用原生的Batch插入省咨,我的天肃弟,竟然只要500多毫秒,也就是0.5秒零蓉,這差距在20倍左右笤受,一直以為是MyBatis-Plus的鍋,原來是MyBatis的鍋壁公,這下舒服了感论,可以安心地用MyBatis-Plus了,除非你不用mybatis了~
2紊册、修改
xml中sql都是由MyBatis-Plus動態(tài)幫我們生成的比肄,包括id為insert的,經(jīng)過測試發(fā)現(xiàn)囊陡,如果我們在xml中自己再新定義一個id為insert的sql語句(注意id必須為insert)芳绩,就將原來的默認(rèn)生成的覆蓋掉了,直接使用我們自己定義的撞反,那么問題就迎刃而解了妥色,具體如下:
a)去掉KeyGenerator實體對象的配置
b)去掉@KeySequence注解
c)id-type: 設(shè)置為AUTO
d)在實體對應(yīng)的xml新增id為insert的sql,類似如下:
<insert id="insert" parameterType="user">
insert into t_user(id, username, password)
values(SEQ_USER.NEXTVAL,#{username},#{password})
</insert>
e)繼續(xù)調(diào)用MyBatis-Plus的saveBatch或save遏片,都會走我們寫的這個insert對應(yīng)的xml
f)測試后1w條大概在幾百毫秒嘹害。
3撮竿、優(yōu)化
(1)主鍵生成策略
IdType.AUTO:表示主鍵自增,適用于數(shù)據(jù)庫支持的自增主鍵笔呀,如 MySQL 的 AUTO_INCREMENT幢踏。
IdType.ASSIGN_ID:使用雪花算法(Snowflake Algorithm)生成主鍵。
IdType.ASSIGN_UUID:生成一個不包含中劃線的 UUID 作為主鍵许师。
IdType.INPUT:表示主鍵值需要手動輸入或設(shè)置房蝉。
參考SEQ_USER.NEXTVAL類似的思路,實際實現(xiàn)的時候微渠,對IdType.ASSIGN_ID進行擴展搭幻,從數(shù)據(jù)庫進行序列化獲取值。在并發(fā)量較大的情況下逞盆,該序列的獲取也是一個瓶頸檀蹋。分析發(fā)現(xiàn)獲取是每次獲取一條,后續(xù)優(yōu)化獲取可以按照遞增的數(shù)量來批量獲取云芦,并在實體組裝的時候续扔,通過單獨調(diào)用批量獲取序號組裝參數(shù)。