Postgresql

PostgreSQL 並發控制和可序列化事務

  • June 22, 2020

我正在建構一個需要對特定表執行一些並發讀/寫操作的應用程序。即使在閱讀了官方文件(以及很多很多文章)之後,問題仍然存在,沒有人談論我遇到的問題(而且我認為這很常見)

我的數據庫:

  • 具有稱為禁用的布爾列的表帳戶(可以忽略其他列)
  • 表請求,表示 2 個帳戶之間的某種關係。一個帳戶可以向另一個帳戶“請求”一些操作。

問題:

  • 帳戶不能請求對已禁用帳戶執行操作。這是一個業務規則,所以我不想將任何與此相關的邏輯放在數據庫中(使用觸發器/約束)。數據庫應該只保證與數據相關的東西(FK、唯一索引等)

由於數據庫不會處理這個業務規則,我的應用程序會。為此,每次發出新請求時,都會發生以下步驟:

  • 打開交易
  • 讀取目標賬戶禁用值 // 並發問題在這裡發生 // 如果目標賬戶在這裡更新它的禁用值會發生什麼?
  • 如果目標帳戶未禁用,則創建一個新的“請求”行

你看到並發問題了嗎?

一開始我嘗試使用Explicit Locking,但是處理起來太複雜了(我的實際情況稍微複雜一點,但是上面的例子就足夠了)。它有效,但不可維護。

之後,我嘗試使用 Serializable 事務,但上面的問題仍然存在,雖然每個人都說“使用 Serializable 事務時,你不必考慮並發”

我必須做些什麼來確保這種一致性?顯式鎖定策略是唯一的方法嗎?

謝謝!

您不會得到可序列化的違規,因為沒有。

如果實際訂單是:

  • T1 開始可序列化
  • T2 開始可序列化
  • T1 看到該帳戶沒有被禁用
  • T2 禁用帳戶
  • T1 插入請求
  • T2 送出
  • T1 送出

上述結果與以下順序兼容:

  • T1 開始可序列化
  • T1 看到該帳戶沒有被禁用
  • T1 插入請求
  • T1 送出
  • T2 開始可序列化
  • T2 禁用帳戶
  • T2 送出

因為結果與某些串列執行兼容,所以它不是違規。如果它兼容的串列執行不是您想要的,那是您的期望錯誤,而不是執行錯誤。

如果 T2 在一個可序列化事務中同時禁用該帳戶並刪除該帳戶的所有待處理請求,那麼這將是違規行為。兩者之一在嘗試送出時會收到錯誤消息。但是,如果 T2 僅禁用帳戶而不刪除針對它的掛起請求,則您的業務邏輯允許該結果,因此這不是違規行為。

如果您在禁用帳戶之前讀取了帳戶的狀態,則會在SERIALIZABLE隔離級別上收到序列化錯誤。所以這將是一種得到你想要的東西的方法。

引用自:https://dba.stackexchange.com/questions/269493