解決spring?data?jpa?saveAll()?保存過(guò)慢問(wèn)題
spring data jpa saveAll() 保存過(guò)慢
問(wèn)題發(fā)現(xiàn)
今天在生產(chǎn)環(huán)境執(zhí)行保存數(shù)據(jù)時(shí) 影響隊(duì)列中其他程序的運(yùn)行 隨后加日志排查 發(fā)現(xiàn) 執(zhí)行 4500條 insert操作時(shí) 耗時(shí) 9分鐘 我類個(gè)去…
解決方案1 此方案在第二天失效了
廢話不多說(shuō) 直接上配置文件參數(shù)
application-prod.yml 部分參數(shù)如下
jpa: show-sql: false hibernate: ddl-auto: none properties: hibernate: jdbc: #為spring data jpa saveAll方法提供批量插入操作 此處可以隨時(shí)更改大小 建議500哦 batch_size: 500 batch_versioned_data: true order_inserts: true
通過(guò)日志打印 執(zhí)行結(jié)果如下
未開批處理 4507條 耗時(shí): 227167ms
開啟批處理 500/次 4507條 耗時(shí): 29140ms
開啟批處理 1000/次 4507條 耗時(shí): 29631ms
以上方案有問(wèn)題,下面附上徹底解決的截圖和記錄
后來(lái)發(fā)現(xiàn)在生產(chǎn)運(yùn)行了一天 還是會(huì)導(dǎo)致保存阻塞的問(wèn)題 100條保存耗時(shí) 9分鐘!!!
數(shù)據(jù)庫(kù)此時(shí)數(shù)據(jù)有300w條
于是分析 saveAll()的源代碼
原來(lái)這個(gè)保存的時(shí)候 會(huì)去數(shù)據(jù)庫(kù)查詢這條數(shù)據(jù)是否存在 如果存在 則修改 不存在則直接添加 如下圖
重寫 saveAll() 的方法 就是仿照它 for循環(huán)里面直接調(diào)用 save()方法
@PersistenceContext() protected EntityManager em;
此處你們可以改成泛型的方式,提取公共類,封裝一下即可。
JPA的saveAll方法執(zhí)行效率很差
springboot項(xiàng)目中使用了SpringDataJpa的技術(shù),很方便,省了很多dao層繁瑣的步驟,但是有一個(gè)接口需要批量更新或者插入,數(shù)據(jù)量挺大,大概1-2w條,每條記錄20-30個(gè)字段吧,對(duì)于剛工作不久的我還是比較大的。我開始使用的saveAll(),因?yàn)楸镜貑卧獪y(cè)試,也沒(méi)考慮那么多(其實(shí)更早期更蠢,遍歷再save,壓根不去考慮數(shù)據(jù)庫(kù)連接池的壓力或者說(shuō)每次遍歷都要去連接數(shù)據(jù)庫(kù)的時(shí)間損耗…),但是客戶壓力測(cè)試,我的接口就拉胯了。接口等待時(shí)間太久…誒
剛開始的思路想解決saveAll方法為什么這么慢的問(wèn)題,因?yàn)閟aveAll是有則更新,無(wú)則新增,所以每條記錄都要去比對(duì)該記錄是否存在表中,效率比較差(我以后還是用JPA去適應(yīng)數(shù)據(jù)量較小的吧,jpa可能還有什么別的路子后面可以配置進(jìn)去也行)。
最后改用了mybatis的foreach方式,雖然比較老套而且肯定不是最快的辦法,但是還是實(shí)用。
使用過(guò)程中,出現(xiàn)了這個(gè)報(bào)錯(cuò):
Packet for query is too large (3227 > 1024). You can change this value on the server by setting the max_allowed_packet' variable.
應(yīng)該是一下子丟進(jìn)來(lái)的數(shù)據(jù)太大,超過(guò)了mysql的限制,有人說(shuō)通過(guò)修改數(shù)據(jù)庫(kù)的max_allowed_packet這個(gè)屬性來(lái)修改,不過(guò)我這里是jenkins部署的服務(wù)器,能操作到數(shù)據(jù)庫(kù)的只有navicat這個(gè)可視化工具,如果使用命令行修改這個(gè)屬性,好像只能暫時(shí)起效。我只好類似分頁(yè),把18000條數(shù)據(jù)按照2000一次批量操作,分成9次,這樣既不會(huì)報(bào)錯(cuò),也會(huì)比savelAll快一些。后續(xù)再研究一下更快更高效的方法。
數(shù)據(jù)庫(kù)配置加上allowMultiQueries=true才會(huì)支持foreach循環(huán)操作。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java之SSM中bean相關(guān)知識(shí)匯總案例講解
這篇文章主要介紹了Java之SSM中bean相關(guān)知識(shí)匯總案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07SpringBoot整合Mybatis簡(jiǎn)單實(shí)現(xiàn)增刪改查
這篇文章主要介紹了SpringBoot整合Mybatis簡(jiǎn)單實(shí)現(xiàn)增刪改查,文章為圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08解決參數(shù)命名不規(guī)范,造成使用@NotNull進(jìn)行校驗(yàn)出現(xiàn)的問(wèn)題
這篇文章主要介紹了解決參數(shù)命名不規(guī)范,造成使用@NotNull進(jìn)行校驗(yàn)出現(xiàn)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java Spring5學(xué)習(xí)之JdbcTemplate詳解
這篇文章主要介紹了Java Spring5學(xué)習(xí)之JdbcTemplate詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-05-05非常詳細(xì)的Java異常處理機(jī)制知識(shí)整理大全
Java異常指在程序運(yùn)行時(shí)可能出現(xiàn)的一些錯(cuò)誤,比如試圖打開一個(gè)根本不存在的文件等,異常處理將會(huì)改變程序的控制流程,讓程序有機(jī)會(huì)對(duì)錯(cuò)誤做出處理,下面這篇文章主要給大家介紹了關(guān)于Java異常處理機(jī)制知識(shí)整理的相關(guān)資料,需要的朋友可以參考下2022-11-11Java?如何用二維數(shù)組創(chuàng)建空心菱形
這篇文章主要介紹了Java?如何用二維數(shù)組創(chuàng)建空心菱形,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Java實(shí)現(xiàn)SMS短信通發(fā)送手機(jī)驗(yàn)證碼案例講解
這篇文章主要介紹了Java實(shí)現(xiàn)SMS短信通發(fā)送手機(jī)驗(yàn)證碼案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08MyBatis注解開發(fā)之實(shí)現(xiàn)自定義映射關(guān)系和關(guān)聯(lián)查詢
本文主要詳細(xì)介紹了MyBatis注解開發(fā)中,實(shí)現(xiàn)自定義映射關(guān)系和關(guān)聯(lián)查詢,文中有詳細(xì)的代碼示例,對(duì)學(xué)習(xí)MyBatis有一定的參考價(jià)值,需要的朋友可以參考閱讀2023-04-04