SpringBoot大事務(wù)問題的常用優(yōu)化方案
1.前言
在分享解決辦法之前,先看看系統(tǒng)中如果出現(xiàn)大事務(wù)可能會(huì)引發(fā)哪些問題

從上圖可以看出,如果系統(tǒng)中出現(xiàn)大事務(wù)的問題,那么情況不容樂觀。
因此,在實(shí)際項(xiàng)目開發(fā)中,我們應(yīng)該盡量避免大事務(wù)的情況。
2.什么是大事務(wù)
大事務(wù)是指運(yùn)行時(shí)間比較長(zhǎng),操作的數(shù)據(jù)比較多的事務(wù)123。例如,執(zhí)行超過5秒、10秒、1分鐘等。大事務(wù)的產(chǎn)生原因包括操作的數(shù)據(jù)比較多、大量的鎖競(jìng)爭(zhēng)、事務(wù)中有其他非數(shù)據(jù)庫(kù)的耗時(shí)操作等。
3.解決辦法
3.1.少用@Transactional注解
在實(shí)際項(xiàng)目中,開啟事務(wù)功能是非常常見的做法。在業(yè)務(wù)方法上加上@Transactional注解,可以方便地開啟事務(wù)功能,這種做法被稱為聲明式事務(wù)。
@Transactional(rollbackFor=Exception.class)
public void save(User user) {
doSameThing...
}為什么說要少用@Transactional注解?
- 我們知道@Transactional注解是通過spring的aop起作用的,但是如果使用不當(dāng),事務(wù)功能可能會(huì)失效。
- @Transactional注解一般加在某個(gè)業(yè)務(wù)方法上,會(huì)導(dǎo)致整個(gè)業(yè)務(wù)方法都在同一個(gè)事務(wù)中,粒度太粗,不好控制事務(wù)范圍,是出現(xiàn)大事務(wù)問題的最常見的原因。
那可以使用編程式事務(wù),在spring項(xiàng)目中使用TransactionTemplate類的對(duì)象,手動(dòng)執(zhí)行事務(wù)。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
doSameThing...
return true;
})
}3.2.將查詢(select)方法放到事務(wù)外
查詢(select)方法一般情況下是不需要事務(wù)的,所以應(yīng)該放到事務(wù)外!
@Transactional(rollbackFor=Exception.class)
public void save(User user) {
query1();
query2();
add1();
update2();
}可以將query1和query2兩個(gè)查詢方法放在事務(wù)外執(zhí)行,將真正需要事務(wù)執(zhí)行的代碼才放到事務(wù)中,比如:add1和update2方法,這樣就能有效的減少事務(wù)的粒度。
可以用這個(gè)TransactionTemplate來解決!
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
query1();
query2();
transactionTemplate.execute((status) => {
add1();
update2();
return Boolean.TRUE;
})
}但是如果你實(shí)在還是想用@Transactional注解,該怎么拆分呢?
public void save(User user) {
query1();
query2();
doSave();
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
add1();
update2();
}在一個(gè)Service內(nèi)部,事務(wù)方法之間的嵌套調(diào)用,普通方法和事務(wù)方法之間的嵌套調(diào)用,都不會(huì)開啟新的事務(wù)。是因?yàn)槁暶魇绞聞?wù)來實(shí)現(xiàn)事務(wù)控制,當(dāng)我們通過this調(diào)用普通方法時(shí),是通過原始對(duì)象而不是代理對(duì)象調(diào)用的方法,導(dǎo)致事務(wù)
不過這邊事務(wù)是失效的,因?yàn)橹苯臃椒ㄕ{(diào)用使用的還是原始對(duì)象,所以事務(wù)不會(huì)生效。
那該怎么解決呢?
1.新加Service方法
這個(gè)方法非常簡(jiǎn)單,只需要新加Service方法,把@Transactional注解加到新Service方法上,把需要事務(wù)執(zhí)行的代碼移到新方法中。具體代碼如下:
@Servcie
publicclass ServiceA {
@Autowired
prvate ServiceB serviceB;
public void save(User user) {
query1();
query2();
serviceB.doSave(user);
}
}
@Servcie
publicclass ServiceB {
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
add1();
update2();
}
}2.在該Service類中注入自己
在該Service類中注入自己也是一種選擇,如下:
@Servcie
publicclass ServiceA {
@Autowired
prvate ServiceA serviceA;
public void save(User user) {
query1();
query2();
serviceA.doSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
add1();
update2();
}
}spring ioc內(nèi)部的三級(jí)緩存保證了它,不會(huì)出現(xiàn)循環(huán)依賴問題。
3.在該Service類中使用AopContext.currentProxy()獲取代理對(duì)象
通過在該Service類中使用AOPProxy獲取代理對(duì)象,實(shí)現(xiàn)相同的功能。如下:
@Servcie
publicclass ServiceA {
public void save(User user) {
query1();
query2();
((ServiceA)AopContext.currentProxy()).doSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
add1();
update2();
}
}3.3.事務(wù)中避免遠(yuǎn)程調(diào)用
網(wǎng)絡(luò)不穩(wěn)定,遠(yuǎn)程調(diào)其他系統(tǒng)響應(yīng)時(shí)間可能比較長(zhǎng),這就是大事務(wù)!
當(dāng)然,發(fā)MQ消息,或者連接redis、mongodb保存數(shù)據(jù)等也屬于遠(yuǎn)程調(diào)用哦!
@Transactional(rollbackFor=Exception.class)
public void save(User user) {
callRemoteApi();
addData1();
}遠(yuǎn)程調(diào)用的代碼可能耗時(shí)較長(zhǎng),切記一定要放在事務(wù)之外。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
callRemoteApi();
transactionTemplate.execute((status) => {
addData1();
return Boolean.TRUE;
})
}不過這就需要建立:重試+補(bǔ)償機(jī)制,達(dá)到數(shù)據(jù)最終一致性了。
3.4.事務(wù)中避免一次性處理太多數(shù)據(jù)
如果一個(gè)事務(wù)中需要處理的數(shù)據(jù)太多,也會(huì)造成大事務(wù)問題。
你可能會(huì)一次批量更新1000條數(shù)據(jù),這樣會(huì)導(dǎo)致大量數(shù)據(jù)鎖等待,特別在高并發(fā)的系統(tǒng)中問題尤為明顯! 那怎么解決呢?
用分頁處理,1000條數(shù)據(jù),分20頁,一次只處理50條數(shù)據(jù),這樣可以大大減少大事務(wù)的出現(xiàn)。
3.5.非事務(wù)執(zhí)行
在使用事務(wù)之前,我們都應(yīng)該思考一下,是不是所有的數(shù)據(jù)庫(kù)操作都需要在事務(wù)中執(zhí)行?
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
addData();
addLog();
updateCount();
return Boolean.TRUE;
})
}上面的例子中,其實(shí)addLog增加操作日志方法 和 updateCount更新統(tǒng)計(jì)數(shù)量方法,是可以不在事務(wù)中執(zhí)行的,因?yàn)椴僮魅罩竞徒y(tǒng)計(jì)數(shù)量這種業(yè)務(wù)允許少量數(shù)據(jù)不一致的情況。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
addData();
return Boolean.TRUE;
})
addLog();
updateCount();
}當(dāng)然大事務(wù)中要鑒別出哪些方法可以非事務(wù)執(zhí)行,其實(shí)沒那么容易,需要對(duì)整個(gè)業(yè)務(wù)梳理一遍,才能找出最合理的答案。
3.6.異步處理
我們都知道,方法同步執(zhí)行需要等待方法返回,如果一個(gè)事務(wù)中同步執(zhí)行的方法太多了,勢(shì)必會(huì)造成等待時(shí)間過長(zhǎng),出現(xiàn)大事務(wù)問題。
看看下面這個(gè)列子:
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
order();
delivery();
return true;
})
}order方法用于下單,delivery方法用于發(fā)貨,是不是下單后就一定要馬上發(fā)貨呢?
答案是否定的。
這里發(fā)貨功能其實(shí)可以走mq異步處理邏輯。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
order();
return Boolean.TRUE;
})
sendMq();
}4.總結(jié)
以上就是SpringBoot大事務(wù)問題的常用優(yōu)化方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot大事務(wù)優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java笛卡爾積算法原理與實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Java笛卡爾積算法原理與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了笛卡爾積算法的原理及java定義與使用笛卡爾積算法的相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
SpringMVC實(shí)現(xiàn)登錄與注冊(cè)功能的詳細(xì)步驟
本文介紹了如何通過Maven配置依賴,創(chuàng)建前端登錄和注冊(cè)頁面,并實(shí)現(xiàn)后端邏輯,詳細(xì)步驟包括配置文件、創(chuàng)建User類、配置中文過濾器及DispatcherServlet,并使用Spring?MVC和JQuery處理前端請(qǐng)求,需要的朋友可以參考下2024-11-11
分析講解SpringMVC注解配置如何實(shí)現(xiàn)
這篇文章主要介紹了本文要介紹用注解方式代替web.xml與SpringMVC的配置文件,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
java通過釘釘機(jī)器人發(fā)消息的實(shí)現(xiàn)示例
本文主要介紹了java通過釘釘機(jī)器人發(fā)消息的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09
在IDEA中配置Selenium和WebDriver的具體操作
在自動(dòng)化測(cè)試領(lǐng)域Selenium是一款非常流行的開源工具,它支持多種瀏覽器,并提供了豐富的API供開發(fā)者使用,而WebDriver則是Selenium的一個(gè)重要組件,它負(fù)責(zé)驅(qū)動(dòng)瀏覽器執(zhí)行測(cè)試腳本,這篇文章主要給大家介紹了在IDEA中配置Selenium和WebDriver的具體操作,需要的朋友可以參考下2024-10-10
Java并發(fā)編程Semaphore計(jì)數(shù)信號(hào)量詳解
這篇文章主要介紹了Java并發(fā)編程Semaphore計(jì)數(shù)信號(hào)量詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10
Java如何獲取發(fā)送請(qǐng)求的電腦的IP地址
文章介紹了如何通過HttpServletRequest獲取客戶端IP地址,特別是當(dāng)客戶端通過代理訪問時(shí),如何使用x-forwarded-for頭來獲取真實(shí)的IP地址2024-11-11
springBoot項(xiàng)目配置文件加載優(yōu)先級(jí)及同配置覆蓋問題詳解
SpringBoot配置?件可以放置在多種路徑下,不同路徑下的配置優(yōu)先級(jí)有所不同,下面這篇文章主要給大家介紹了關(guān)于springBoot項(xiàng)目配置文件加載優(yōu)先級(jí)及同配置覆蓋問題的相關(guān)資料,需要的朋友可以參考下2023-05-05
SpringBoot基于Mybatis-Plus自動(dòng)代碼生成
這篇文章主要介紹了SpringBoot基于Mybatis-Plus自動(dòng)代碼生成,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04

