欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

為什么Spring官方推薦的@Transational還能導致生產事故

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

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

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

@Transactional導致的生產事故

19年在公司做了一個內部報銷的項目,有這樣一個業(yè)務邏輯:

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

2、員工可以在報銷平臺勾選自己打車費用并創(chuàng)建一張報銷單進行報銷,創(chuàng)建報銷單的同時會創(chuàng)建一條審批流(統(tǒng)一流程平臺)讓領導審批

當時創(chuàng)建報銷單的代碼是這么寫的:

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

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

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

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

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

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

事故原因分析

通過對日志的分析我們很容易就可以定位到故障原因就是保存報銷單的save()方法,而罪魁禍首就是那個@Transactional注解。

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

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

在一個事務中執(zhí)行RPC操作導致數據庫連接池撐爆屬于是典型的長事務問題,類似的操作還有在事務中進行大量數據查詢,業(yè)務規(guī)則處理等…

何為長事務?

顧名思義就是運行時間比較長,長時間未提交的事務,也可以稱之為大事務。

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

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

  • 數據庫連接池被占滿,應用無法獲取連接資源;
  • 容易引發(fā)數據庫死鎖;
  • 數據庫回滾時間長;
  • 在主從架構中會導致主從延時變大。

如何避免長事務?

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

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

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

聲明式事務

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

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

聲明式事務有一個最大的缺點,就是事務的顆粒度是整個方法,無法進行精細化控制。

與聲明式事務對應的就是編程式事務。

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

@Autowired 
private TransactionTemplate transactionTemplate; 
 
... 

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

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

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

有的同學會說,@Transactional使用這么簡單,有沒有辦法既可以使用@Transactional,又能避免產生長事務?

image-20211115234407264

那就需要對方法進行拆分,將不需要事務管理的邏輯與事務操作分開:

@Service
public class OrderService{

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

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

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

  • @Transactional 應用在非 public 修飾的方法上
  • @Transactional 注解屬性 propagation 設置錯誤
  • @Transactional 注解屬性 rollbackFor 設置錯誤
  • 同一個類中方法調用,導致@Transactional失效
  • 異常被catch捕獲導致@Transactional失效

所以正確的拆分方法應該是下面兩種:

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

@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),方法內使用AopContext.currentProxy()獲得代理類,使用事務。

SpringBootApplication.java

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

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

小結

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

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

相關文章

  • java Swing實現五子棋游戲

    java Swing實現五子棋游戲

    這篇文章主要為大家詳細介紹了java Swing實現五子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • SpringBoot獲取配置信息的三種方式總結

    SpringBoot獲取配置信息的三種方式總結

    這篇文章給大家介紹了SpringBoot獲取配置信息的三種方式,@Value屬性值注入,綁定配置類和通過 environment獲取這三種方式,文中通過代碼示例給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-01-01
  • springmvc mybatis集成配置示例

    springmvc mybatis集成配置示例

    本文主要介紹springmvc+mybatis集成配置,這里提供了實例代碼,和簡單說明,有需要的小伙伴可以參考下
    2016-09-09
  • 解決springboot mapper注入報紅問題

    解決springboot mapper注入報紅問題

    這篇文章主要介紹了解決springboot mapper注入報紅問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Mybatis如何使用正則模糊匹配多個數據

    Mybatis如何使用正則模糊匹配多個數據

    這篇文章主要介紹了Mybatis如何使用正則模糊匹配多個數據,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • java 如何實現日志追蹤MDC

    java 如何實現日志追蹤MDC

    這篇文章主要介紹了java 實現日志追蹤MDC方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 簡單易懂講解happens-before原則

    簡單易懂講解happens-before原則

    Java內存模型中的happens-before是什么?為什么會有這東西的存在?一個新東西肯定是上手先,但是等我們空下來回過頭來,我們還是需要去理解這些知識,只有這樣我才能深刻的記住,并且運用熟練。下來和小編來一起學習下
    2019-05-05
  • SpringBoot實現多個ApplicationRunner時部分接口未執(zhí)行問題

    SpringBoot實現多個ApplicationRunner時部分接口未執(zhí)行問題

    這篇文章主要介紹了SpringBoot實現多個ApplicationRunner時部分接口未執(zhí)行問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Bean的自動注入及循環(huán)依賴問題

    Bean的自動注入及循環(huán)依賴問題

    本文詳細介紹了Bean的自動注入及循環(huán)依賴,文中通過代碼介紹的非常詳細,對大家的學習有一定的研究價值,感興趣的小伙伴可以閱讀參考
    2023-03-03
  • 深入解析Java的Hibernate框架中的一對一關聯映射

    深入解析Java的Hibernate框架中的一對一關聯映射

    這篇文章主要介紹了Java的Hibernate框架的一對一關聯映射,包括對一對一外聯映射的講解,需要的朋友可以參考下
    2016-01-01

最新評論