2014-09-11 63 views
1

我有一個Web服務和幾個後臺運行的異步服務(用於長時間運行的數據收集過程),它們使用相同的DAO庫和共享數據源。使用Spring JDBC模板實現的DAO庫。 RDBMS是PostgreSQL。
當Web服務和異步服務通過DAO同時修改一個表中的相同行並且我有不一致的數據時,這並不罕見。
例如我有一個實體中的「狀態」字段,其值可以爲:1-付費,2-未付。

有時我有這樣的情況:如何處理Web服務中的併發數據修改混亂和合並(樂觀/悲觀鎖定)?

  • 交易#1:web服務正在修改從 1至2中的 「狀態」 的值與ID到行= 1
  • 交易#2:在同一時間異步服務是抓住一些端點一些 數據和修改行的另一場與ID = 1,但「狀態」欄檻保持1.它不知道,「狀態」值從1變在交易#1中爲2。

因爲我有「國家」等於1,但它必須是2.它也有爭議。有時候異步服務會改變「狀態」字段,Web服務不知道這個改變,並再次造成數據混亂。當然,這不僅發生在國家領域。

我有兩個選擇:

  1. 使用悲觀SELECT ... FOR UPDATE。
    但它不適合我 ,因爲Web服務通常會從表中獲取部分行。它 不能等待很長時間,而異步服務持有 鎖定,因爲這裏的性能至關重要。
  2. 使用樂觀鎖定。 「版本」字段等。
    但是我不能只是在樂觀鎖定失敗的情況下回滾 更改,因爲必須合併此更改 。有時候我不能重複操作,因爲另一個系統中的原子操作不屬於spring jdbc事務。

可能存在一些模式來處理這樣的情況下,合併數據?

謝謝
伊萬

+0

這是不完全重複的,因爲這個問題是關於一個長期運行的操作對同一數據庫的多個短的問題,。這一點是**沒有**由另一個問題或其答案解決。 – 2014-09-16 06:14:21

回答

2

這是我使用的作爲經驗法則 - 意味着它可以適應特殊用途...

  • 一個單一的交易中(和我有每個請求一個單寫事務):悲觀鎖,因爲所有這些交易都是短(他們真的要)等獲得鎖的延遲是可以接受的,優於有回滾和重試異常(*)
  • 兩個不同的請求處理(GET展現形式和其提交的POST/PUT)之間的情況:樂觀鎖定,因爲我不想讓真正的數據庫鎖很長一段時間

這是相當簡單,連貫,我只重新思考,如果事情真的出問題了。

在單個事務內部進行樂觀鎖定的唯一用例是,如果您的併發請求數量非常高,並且每個用戶都更新自己的數據,那麼爭用風險很小 - 但您只能獲得鎖定階段,因爲等待鎖定的概率同樣很低。

(*)如果你得到一個樂觀鎖定異常,你必須回滾事務,你不能夠允許簡單地報告錯誤,或者你必須重試的次數。

編輯:

如果我理解正確的,你的問題是,你有一個長期運行的批量操作和短Web服務請求。

恕我直言,有三種方法可以解決這個問題,但目前還沒有足夠的信息來選擇哪個是最適合你的使用情況在批

  • 使用的許多短事務。這樣,如果同時更新的概率較低,則可以使用樂觀鎖定(使用重試),如果沒有太多同時發生的Web服務請求,則可以使用悲觀鎖定。這是最簡單的方法,但如果您的業務規則要求該批次使用單個事務,則可能無法使用。在批次和Web服務請求
  • 使用悲觀鎖,但與Web服務部分短暫停。這樣,如果您獲得Web服務中的鎖定,則確定沒有批處理正在修改相同的值並可以安全地提交。一旦批量鎖定,您確定在提交之前,沒有任何Web服務會修改該值。如果Web服務無法獲得鎖定,則只需向調用者返回錯誤。唯一的要求是Web服務的客戶端稍後可以重試它的請求。
  • 不立即執行提交Web服務的交易,但他們入隊,並在批量操作結束時處理它們。通過這種方式,您可以確保Web服務立即返回,並且不會發生併發更新。但是,您必須提供一種機制,允許Web服務的客戶稍後諮詢其請求的結果(如果它們可能被拒絕),並且無論如何,它假設請求可以被推遲。這對於銀行賬戶存款可以很好地工作,需要反饋回扣,並且不能用於在飛機上預訂座位。
+0

感謝您的回答。由於我寫的問題比較複雜,因此選擇了樂觀和悲觀鎖定。嘗試在例子中解釋。異步服務開始執行並從數據庫中選擇一批行。然後它調用一些Web服務來獲取一些數據來豐富這些行。 Web服務調用需要很長時間。並且在所有更新服務之後保存行。我在這裏有很長時間的交易,第三方Web服務電話不是交易的一部分。如果我將重複這個調用,那麼我將在第三方系統中獲得數據重複。 – Ivan 2014-09-15 09:34:18

+0

@Ivan:Oups起初我沒有注意到它,但看起來您的問題如下所示:您對具有相同數據庫*的修改可能來自具有短事務的Web服務以及具有長時間運行事務的批處理。你可以證實這一點,它是說批次運行多久? – 2014-09-15 13:00:57

+0

是的,我確認。我有兩個問題在這裏: 1)我有3個異步服務是批量或逐一更新使用第三方服務每天在同一表中的行,並且足夠經常並行。選擇行並更新它們之間的時間間隔太長。由於與第三方服務的交互非常緩慢,從10秒到10分鐘。 2)在同一張表中有些行正在改變行的狀態的支付交易。 看起來像第一個解決方案將是一個很好的適合我,我會嘗試。謝謝。 – Ivan 2014-09-16 09:42:50

0

圍棋與樂觀鎖..當更新由於版本不匹配失敗..相反,重試,直到成功爲止不要回滾。 您可能有一個重試次數的上限..

+0

除非您使用保存點,否則回滾事務是您在Postgres中的唯一選項。一旦交易出現錯誤,您不能「繼續」 – 2014-09-15 06:11:03

+0

我不能只是回滾。例如。服務A選擇一些行並處理一些長時間運行的過程,並在最後對行進行一批更新。同時服務B正在爲已經服務A正在使用的這些行中的一個付款。現在我們看到服務B將狀態從未支付變爲付費並將更改提交到數據庫,但服務B不知道有關更改,並且只會在承諾自己的更改時從支付到未支付狀態重寫狀態。這兩項服務都使用第三方服務,我無法再重複這些操作。我需要某種合併。 – Ivan 2014-09-15 09:37:13