簡體   English   中英

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

[英]How to make multiple call of @Transactional method to a single transaction

我有一個方法

@Transactional
public void updateSharedStateByCommunity(List[]idList)

從以下REST API調用此方法:

@RequestMapping(method = RequestMethod.POST)
public ret_type updateUser(param) {
  // call updateSharedStateByCommunity
}

現在 ID 列表非常大,比如 200000,當我嘗試處理它時,它需要很多時間並且在客戶端發生超時錯誤。

因此,我想將其拆分為兩個調用,每個調用的列表大小為 100000。

但是,問題是,它被認為是 2 個獨立的交易。

注意:2 次調用是一個例子,如果數字 id 更大,它可以分為多次。

我需要確保對單個事務的兩個單獨調用。 如果 2 個調用中的任何一個失敗,則它應該回滾到所有操作。

另外,在客戶端,我們需要顯示進度對話框,所以我不能只使用超時。

對您的問題 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); 
    }
}

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

所以它滿足了苛刻的要求:你想要一個單一的交易——你已經得到了。 現在所有代碼都在同一個事務中運行。 現在每個方法都以 100000 運行,而不是所有的 id,一切都是同步的:)

然而,由於許多不同的原因,這種設計是有問題的。

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

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

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

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

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

這樣與客戶端的交互就會發生變化:

  1. 客戶使用 200000 個 ID 調用“updateUser”
  2. 服務“立即”響應“我收到了您的請求,這是一個請求 ID,偶爾 ping 我看看會發生什么。
  3. 服務啟動異步任務並在單個事務中逐塊處理數據
  4. 客戶端使用該 ID 調用“get”方法,服務器從共享的 state 讀取進度。
  5. 一旦准備好,“Get”方法將響應“done”。

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

您還可以使用更現代的技術來通知服務器(例如 web sockets),但對於這個問題,它有點超出 scope 。

這里要考慮的另一件事:據我所知,處理 200000 個對象應該在 30 分鍾內完成,這對於現代 RDBMS 來說並沒有那么多。 當然,在不了解您的用例的情況下很難說出那里發生了什么,但也許您可以優化流程本身(使用批量操作,減少對數據庫的請求數量,緩存等等)。

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

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

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

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM