亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何對單個事務多次調用@Transactional方法

如何對單個事務多次調用@Transactional方法

暮色呼如 2023-10-13 10:30:54
我有一個方法@Transactionalpublic void updateSharedStateByCommunity(List[]idList)從以下 REST API 調用此方法:@RequestMapping(method = RequestMethod.POST)public ret_type updateUser(param) {  // call updateSharedStateByCommunity}現在ID列表非常大,比如200000,當我嘗試處理它時,需要花費很多時間并且在客戶端發生超時錯誤。因此,我想將其拆分為兩個調用,每個調用的列表大小為 100000。但是,問題是,它被視為兩個獨立的交易。注意:2次呼叫只是一個例子,如果號碼id更大的話,它可以分為多次。我需要確保對單個事務進行兩次單獨的調用。如果這 2 個調用中的任何一個失敗,那么它應該回滾到所有操作。另外,在客戶端,我們需要顯示進度對話框,所以我不能只使用超時。
查看完整描述

4 回答

?
繁星點點滴滴

TA貢獻1803條經驗 獲得超3個贊

IMO 對您的問題最明顯的直接答案是稍微更改代碼:


@RequestMapping(method = RequestMethod.POST)

public ret_type updateUser(param) {

    updateSharedStateByCommunityBlocks(resolveIds);

}


...


And in Service introduce a new method (if you can't change the code of the service provide an intermediate class that you'll call from controller with the following functionality):


@Transactional

public updateSharedStatedByCommunityBlocks(resolveIds) {

    List<String> [] blocks = split(resolveIds, 100000);  // 100000 - bulk size

    for(List<String> block :blocks) {

       updateSharedStateByCommunity(block); 

    }

}

如果此方法位于同一服務中,則@Transactional原始方法updateSharedStateByCommunity不會執行任何操作,因此它會起作用。如果您將此代碼放入其他類中,那么它將起作用,因為 spring 事務的默認傳播級別是“必需”

因此它滿足了苛刻的要求:您想要進行一次交易 - 您已經做到了?,F在所有代碼都在同一個事務中運行?,F在每個方法都使用 100000 個 ID 運行,而不是使用所有 id,一切都是同步的:)

然而,這種設計由于許多不同的原因而存在問題。

  1. 正如您在問題的最后一句中所說,它不允許跟蹤進度(向用戶顯示)。REST 是同步的。

  2. 它假設網絡是可靠的,并且等待 30 分鐘在技術上不是問題(不考慮 UX 和必須等待的“緊張”用戶:))

  3. 除此之外,網絡設備可以強制關閉連接(例如具有預先配置的請求超時的負載均衡器)。

這就是為什么人們建議某種異步流。

我可以說,您仍然可以使用異步流,生成任務,并在每次批量更新后一些共享狀態(在單個實例的情況下在內存中)和持久狀態(如集群情況下的數據庫)。

這樣與客戶端的交互就會改變:

  1. 客戶端使用 200000 個 id 調用“updateUser”

  2. 服務會“立即”響應,例如“我收到了您的請求,這是一個請求 ID,請偶爾對我進行 ping 操作,看看會發生什么情況。

  3. 服務啟動異步任務并在單個事務中逐塊處理數據

  4. 客戶端使用該 id 調用“get”方法,服務器從共享狀態讀取進度。

  5. 一旦準備好,“獲取”方法將響應“完成”。

如果事務執行期間出現故障,則回滾完成,并且進程將數據庫狀態更新為“失敗”。

您還可以使用更現代的技術來通知服務器(例如網絡套接字),但這超出了這個問題的范圍。

這里需要考慮的另一件事是:據我所知,處理 200000 個對象應該在不到 30 分鐘的時間內完成,對于現代 RDBMS 來說還不夠。當然,在不知道您的用例的情況下,很難判斷那里發生了什么,但也許您可以優化流程本身(使用批量操作、減少對數據庫的請求數量、緩存等)。


查看完整回答
反對 回復 2023-10-13
?
四季花海

TA貢獻1811條經驗 獲得超5個贊

在這些場景中,我的首選方法是使調用異步(Spring Boot 允許使用注釋@Async),因此客戶端不會期望任何 HTTP 響應。該通知可以通過 WebSocket 完成,該 WebSocket 將向客戶端推送一條消息,其中包含每個 X 項的處理進度。

當然,它會給您的應用程序增加更多的復雜性,但如果您正確設計該機制,您將能夠將其重用于您將來可能面臨的任何其他類似操作。


查看完整回答
反對 回復 2023-10-13
?
MYYA

TA貢獻1868條經驗 獲得超4個贊

從技術角度來看,可以通過傳播來完成org.springframework.transaction.annotation.Propagation#NESTED,NESTED 行為使嵌套 Spring 事務使用相同的物理事務,但在嵌套調用之間設置保存點,因此內部事務也可以獨立于外部事務回滾,或者讓它們傳播。但限制僅適用于org.springframework.jdbc.datasource.DataSourceTransactionManager數據源。

但是對于非常大的數據集,它仍然需要更多的時間來處理并使客戶端等待,因此從解決方案的角度來看,也許使用異步方法會更好,但這取決于您的要求。


查看完整回答
反對 回復 2023-10-13
?
一只斗牛犬

TA貢獻1784條經驗 獲得超2個贊

@Transactional注釋接受 atimeout(盡管并非所有底層實現都支持它)。我反對嘗試將 ID 拆分為兩個調用,而是嘗試修復超時(畢竟,您真正想要的是單個、全有或全無的事務)。您可以為整個應用程序設置超時,而不是針對每個方法設置超時。



查看完整回答
反對 回復 2023-10-13
  • 4 回答
  • 0 關注
  • 236 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號