最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

為什么Spring官方推薦的@Transational還能導(dǎo)致生產(chǎn)事故

 更新時(shí)間:2021年11月26日 09:12:52   作者:飄渺Jam  
在Spring中進(jìn)行事務(wù)管理非常簡單,只需要在方法上加上注解@Transactional,那么為什么Spring官方推薦的@Transational還能導(dǎo)致生產(chǎn)事故,本文就詳細(xì)的介紹一下

在Spring中進(jìn)行事務(wù)管理非常簡單,只需要在方法上加上注解@Transactional,Spring就可以自動幫我們進(jìn)行事務(wù)的開啟、提交、回滾操作。甚至很多人心里已經(jīng)將Spring事務(wù)與@Transactional劃上了等號,只要有數(shù)據(jù)庫相關(guān)操作就直接給方法加上@Transactional注解。

不瞞你說,我之前也一直是這樣,直到使用@Transactional導(dǎo)致了一次生產(chǎn)事故,而那次生產(chǎn)事故還導(dǎo)致我當(dāng)月績效被打了D…

@Transactional導(dǎo)致的生產(chǎn)事故

19年在公司做了一個(gè)內(nèi)部報(bào)銷的項(xiàng)目,有這樣一個(gè)業(yè)務(wù)邏輯:

1、員工加班打車可以通過滴滴出行企業(yè)版直接打車,第二天打車費(fèi)用可以直接同步到我們的報(bào)銷平臺

2、員工可以在報(bào)銷平臺勾選自己打車費(fèi)用并創(chuàng)建一張報(bào)銷單進(jìn)行報(bào)銷,創(chuàng)建報(bào)銷單的同時(shí)會創(chuàng)建一條審批流(統(tǒng)一流程平臺)讓領(lǐng)導(dǎo)審批

當(dāng)時(shí)創(chuàng)建報(bào)銷單的代碼是這么寫的:

 /**
 * 保存報(bào)銷單并創(chuàng)建工作流
 */
@Transactional(rollbackFor = Exception.class)
public void save(RequestBillDTO requestBillDTO){
     //調(diào)用流程HTTP接口創(chuàng)建工作流
    workflowUtil.createFlow("BILL",requestBillDTO);
    
    //轉(zhuǎn)換DTO對象
    RequestBill requestBill = JkMappingUtils.convert(requestBillDTO, RequestBill.class);
    requestBillDao.save(requestBill);
    //保存明細(xì)表
    requestDetailDao.save(requestBill.getDetail())
}

代碼非常簡單也很 “優(yōu)雅”,先通過http接口調(diào)用工作流引擎創(chuàng)建審批流,然后保存報(bào)銷單,而為了保證操作的事務(wù),在整個(gè)方法上加上了@Transactional注解(仔細(xì)想想,這樣真的能保證事務(wù)嗎?)。

報(bào)銷項(xiàng)目屬于公司內(nèi)部項(xiàng)目,本身是沒什么高并發(fā)的,系統(tǒng)也一直穩(wěn)定運(yùn)行著。

在年末的一天下午(前幾天剛好下了大雪,打車的人特別多),公司發(fā)通知郵件說年度報(bào)銷窗口即將關(guān)閉,需要盡快將未報(bào)銷的費(fèi)用報(bào)銷掉,而剛好那天工作流引擎在進(jìn)行安全加固。

收到郵件后報(bào)銷的人開始逐漸增多,在接近下班的時(shí)候到達(dá)頂峰,此時(shí)報(bào)銷系統(tǒng)開始出現(xiàn)了故障:數(shù)據(jù)庫監(jiān)控平臺一直收到告警短信,數(shù)據(jù)庫連接不足,出現(xiàn)大量死鎖;日志顯示調(diào)用流程引擎接口出現(xiàn)大量超時(shí);同時(shí)一直提示CannotGetJdbcConnectionException,數(shù)據(jù)庫連接池連接占滿。

在發(fā)生故障后,我們嘗試過殺掉死鎖進(jìn)程,也進(jìn)行過暴力重啟,只是不到10分鐘故障再次出現(xiàn),收到大量電話投訴。最后沒辦法只能向全員發(fā)送停機(jī)維護(hù)郵件并發(fā)送故障報(bào)告,而后,績效被打了個(gè)D,慘…。

事故原因分析

通過對日志的分析我們很容易就可以定位到故障原因就是保存報(bào)銷單的save()方法,而罪魁禍?zhǔn)拙褪悄莻€(gè)@Transactional注解。

我們知道@Transactional 注解,是使用 AOP 實(shí)現(xiàn)的,本質(zhì)就是在目標(biāo)方法執(zhí)行前后進(jìn)行攔截。 在目標(biāo)方法執(zhí)行前加入或創(chuàng)建一個(gè)事務(wù),在執(zhí)行方法執(zhí)行后,根據(jù)實(shí)際情況選擇提交或是回滾事務(wù)。

當(dāng) Spring 遇到該注解時(shí),會自動從數(shù)據(jù)庫連接池中獲取 connection,并開啟事務(wù)然后綁定到 ThreadLocal 上,對于@Transactional注解包裹的整個(gè)方法都是使用同一個(gè)connection連接。如果我們出現(xiàn)了耗時(shí)的操作,比如第三方接口調(diào)用,業(yè)務(wù)邏輯復(fù)雜,大批量數(shù)據(jù)處理等就會導(dǎo)致我們我們占用這個(gè)connection的時(shí)間會很長,數(shù)據(jù)庫連接一直被占用不釋放。一旦類似操作過多,就會導(dǎo)致數(shù)據(jù)庫連接池耗盡。

在一個(gè)事務(wù)中執(zhí)行RPC操作導(dǎo)致數(shù)據(jù)庫連接池?fù)伪瑢儆谑堑湫偷?strong>長事務(wù)問題,類似的操作還有在事務(wù)中進(jìn)行大量數(shù)據(jù)查詢,業(yè)務(wù)規(guī)則處理等…

何為長事務(wù)?

顧名思義就是運(yùn)行時(shí)間比較長,長時(shí)間未提交的事務(wù),也可以稱之為大事務(wù)。

長事務(wù)會引發(fā)哪些問題?

長事務(wù)引發(fā)的常見危害有:

  • 數(shù)據(jù)庫連接池被占滿,應(yīng)用無法獲取連接資源;
  • 容易引發(fā)數(shù)據(jù)庫死鎖;
  • 數(shù)據(jù)庫回滾時(shí)間長;
  • 在主從架構(gòu)中會導(dǎo)致主從延時(shí)變大。

如何避免長事務(wù)?

既然知道了長事務(wù)的危害,那如何在開發(fā)中避免出現(xiàn)長事務(wù)問題呢?

很明顯,解決長事務(wù)的宗旨就是 對事務(wù)方法進(jìn)行拆分,盡量讓事務(wù)變小,變快,減小事務(wù)的顆粒度。

既然提到了事務(wù)的顆粒度,我們就先回顧一下Spring進(jìn)行事務(wù)管理的方式。

聲明式事務(wù)

首先我們要知道,通過在方法上使用@Transactional注解進(jìn)行事務(wù)管理的操作叫聲明式事務(wù) 。

使用聲明式事務(wù)的優(yōu)點(diǎn) 很明顯,就是使用很簡單,可以自動幫我們進(jìn)行事務(wù)的開啟、提交以及回滾等操作。使用這種方式,程序員只需要關(guān)注業(yè)務(wù)邏輯就可以了。

聲明式事務(wù)有一個(gè)最大的缺點(diǎn),就是事務(wù)的顆粒度是整個(gè)方法,無法進(jìn)行精細(xì)化控制。

與聲明式事務(wù)對應(yīng)的就是編程式事務(wù)。

基于底層的API,開發(fā)者在代碼中手動的管理事務(wù)的開啟、提交、回滾等操作。在spring項(xiàng)目中可以使用TransactionTemplate類的對象,手動控制事務(wù)。

@Autowired 
private TransactionTemplate transactionTemplate; 
 
... 

public void save(RequestBill requestBill) { 
    transactionTemplate.execute(transactionStatus -> {
        requestBillDao.save(requestBill);
        //保存明細(xì)表
        requestDetailDao.save(requestBill.getDetail());
        return Boolean.TRUE; 
    });
} 

使用編程式事務(wù)最大的好處就是可以精細(xì)化控制事務(wù)范圍。

所以避免長事務(wù)最簡單的方法就是不要使用聲明式事務(wù)@Transactional,而是使用編程式事務(wù)手動控制事務(wù)范圍。

有的同學(xué)會說,@Transactional使用這么簡單,有沒有辦法既可以使用@Transactional,又能避免產(chǎn)生長事務(wù)?

image-20211115234407264

那就需要對方法進(jìn)行拆分,將不需要事務(wù)管理的邏輯與事務(wù)操作分開:

@Service
public class OrderService{

    public void createOrder(OrderCreateDTO createDTO){
        query();
        validate();
        saveData(createDTO);
    }
  
		//事務(wù)操作
    @Transactional(rollbackFor = Throwable.class)
    public void saveData(OrderCreateDTO createDTO){
        orderDao.insert(createDTO);
    }
}

query()validate()不需要事務(wù),我們將其與事務(wù)方法saveData()拆開。

當(dāng)然,這種拆分會命中使用@Transactional注解時(shí)事務(wù)不生效的經(jīng)典場景,很多新手非常容易犯這個(gè)錯(cuò)誤。@Transactional注解的聲明式事務(wù)是通過spring aop起作用的,而spring aop需要生成代理對象,直接在同一個(gè)類中方法調(diào)用使用的還是原始對象,事務(wù)不生效。其他幾個(gè)常見的事務(wù)不生效的場景為:

  • @Transactional 應(yīng)用在非 public 修飾的方法上
  • @Transactional 注解屬性 propagation 設(shè)置錯(cuò)誤
  • @Transactional 注解屬性 rollbackFor 設(shè)置錯(cuò)誤
  • 同一個(gè)類中方法調(diào)用,導(dǎo)致@Transactional失效
  • 異常被catch捕獲導(dǎo)致@Transactional失效

所以正確的拆分方法應(yīng)該是下面兩種:

可以將方法放入另一個(gè)類,如新增 manager層,通過spring注入,這樣符合了在對象之間調(diào)用的條件。

@Service
public class OrderService{
  
    @Autowired
  	private OrderManager orderManager;

    public void createOrder(OrderCreateDTO createDTO){
        query();
        validate();
        orderManager.saveData(createDTO);
    }
}

@Service
public class OrderManager{
  
    @Autowired
  	private OrderDao orderDao;
  
		@Transactional(rollbackFor = Throwable.class)
    public void saveData(OrderCreateDTO createDTO){
        orderDao.saveData(createDTO);
    }
}

啟動類添加@EnableAspectJAutoProxy(exposeProxy = true),方法內(nèi)使用AopContext.currentProxy()獲得代理類,使用事務(wù)。

SpringBootApplication.java

@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}

OrderService.java
  
public void createOrder(OrderCreateDTO createDTO){
    OrderService orderService = (OrderService)AopContext.currentProxy();
    orderService.saveData(createDTO);
}

小結(jié)

使用@Transactional注解在開發(fā)時(shí)確實(shí)很方便,但是稍微不注意就可能出現(xiàn)長事務(wù)問題。所以對于復(fù)雜業(yè)務(wù)邏輯,我這里更建議你使用編程式事務(wù)來管理事務(wù),當(dāng)然,如果你非要使用@Transactional,可以根據(jù)上文提到的兩種方案進(jìn)行方法拆分。

到此這篇關(guān)于為什么Spring官方推薦的@Transational還能導(dǎo)致生產(chǎn)事故的文章就介紹到這了,更多相關(guān)Spring @Transationa 生產(chǎn)事故內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解

    MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解

    本文將為大家說說關(guān)于 #{} 和 ${},這個(gè)是 MyBatis 在面試中最常問的面試題,以及數(shù)據(jù)庫連接池相關(guān)的知識,感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • vue用CryptoJS加密,java用CryptoUtil解密

    vue用CryptoJS加密,java用CryptoUtil解密

    CryptoJS是一個(gè)JavaScript庫,提供了一系列密碼學(xué)函數(shù)和工具,用于加密、解密、生成摘要等任務(wù),本文vue前端使用CryptoJS加密,java后端使用CryptoUtil解密
    2024-09-09
  • springboot使用Logback把日志輸出到控制臺或輸出到文件

    springboot使用Logback把日志輸出到控制臺或輸出到文件

    這篇文章給大家介紹springboot項(xiàng)目使用日志工具Logback把日志不僅輸出到控制臺,也可以輸出到文件的操作方法,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-10-10
  • 基于常用json框架介紹和Jackson返回結(jié)果處理方式

    基于常用json框架介紹和Jackson返回結(jié)果處理方式

    這篇文章主要介紹了基于常用json框架介紹和Jackson返回結(jié)果處理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • idea項(xiàng)目debug模式啟動,斷點(diǎn)失效,斷點(diǎn)紅點(diǎn)內(nèi)無對勾問題及解決

    idea項(xiàng)目debug模式啟動,斷點(diǎn)失效,斷點(diǎn)紅點(diǎn)內(nèi)無對勾問題及解決

    這篇文章主要介紹了idea項(xiàng)目debug模式啟動,斷點(diǎn)失效,斷點(diǎn)紅點(diǎn)內(nèi)無對勾問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Java并發(fā)編程回環(huán)屏障CyclicBarrier

    Java并發(fā)編程回環(huán)屏障CyclicBarrier

    這篇文章主要介紹了Java并發(fā)編程回環(huán)屏障CyclicBarrier,文章繼續(xù)上文所介紹的Java并發(fā)編程同步器CountDownLatch展開主題相關(guān)內(nèi)容,需要的小伙伴可以參考一下
    2022-04-04
  • java實(shí)現(xiàn)ArrayList根據(jù)存儲對象排序功能示例

    java實(shí)現(xiàn)ArrayList根據(jù)存儲對象排序功能示例

    這篇文章主要介紹了java實(shí)現(xiàn)ArrayList根據(jù)存儲對象排序功能,結(jié)合實(shí)例形式分析了java針對ArrayList的相關(guān)運(yùn)算、排序操作技巧,需要的朋友可以參考下
    2018-01-01
  • MyEclipse2018中安裝Mybatis generator插件的實(shí)現(xiàn)步驟

    MyEclipse2018中安裝Mybatis generator插件的實(shí)現(xiàn)步驟

    這篇文章主要介紹了MyEclipse2018中安裝Mybatis generator插件的實(shí)現(xiàn)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • SpringCloud配置刷新原理解析

    SpringCloud配置刷新原理解析

    這篇文章主要介紹了SpringCloud之配置刷新的原理,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-05-05
  • Java中關(guān)于二叉樹層序遍歷深入了解

    Java中關(guān)于二叉樹層序遍歷深入了解

    二叉樹的層序遍歷是面試經(jīng)常會被考察的知識點(diǎn),甚至要求當(dāng)場寫出實(shí)現(xiàn)過程。層序遍歷所要解決的問題很好理解,就是按二叉樹從上到下,從左到右依次打印每個(gè)節(jié)點(diǎn)中存儲的數(shù)據(jù),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09

最新評論

丰原市| 丽水市| 崇左市| 康马县| 七台河市| 寿阳县| 常州市| 靖安县| 大方县| 象山县| 南投县| 旬阳县| 盐边县| 米易县| 常山县| 汶上县| 广丰县| 灵丘县| 攀枝花市| 大理市| 桂东县| 梁山县| 秀山| 竹北市| 杂多县| 蒙自县| 桂阳县| 鸡东县| 乡城县| 务川| 南召县| 固镇县| 弥渡县| 北票市| 长沙县| 资溪县| 仁寿县| 财经| 咸阳市| 平利县| 邯郸县|