2017-02-18 60 views
3

我已經運行到偶爾死鎖在我的應用程序,因爲兩筆交易這需要更新同一行,但在不同的順序(例如,事務A更新行X然後Ÿ,而事務B更新行ÿ然後X)。依靠Postgres的併發控制死鎖檢測安全嗎?

對於各種原因,解決避免這種死鎖的傳統方法 - 鎖定或以一致的順序更新行 - 並不理想。

由於我試圖執行的更新以其他方式是冪等和順序無關的,因此在應用程序級別簡單捕獲這些偶爾的死鎖並重試事務是安全合理的嗎?

例如:

def process_update(update): 
    attempt = 0 
    while attempt < 10: 
     try: 
      execute("SAVEPOINT foo") 
      for row in update: 
       execute("UPDATE mytable SET … WHERE …", row) 
      execute("RELEASE SAVEPOINT foo") 
      break 
     except Deadlock: 
      execute("ROLLBACK TO SAVEPOINT foo") 
     attempt += 1 
    raise Exception("Too many retries") 

這是一個合理的想法?還是有與Postgres的死鎖檢測相關的成本,可能會使它危險?

回答

1

對於在同一張表上運行50到100個併發進程的系統,我做了大量的研究和實驗。除了基本的死鎖之外,還有許多事務失敗。我的情況包括讀取已提交和可序列化的事務。沒有任何情況在應用程序級別處理這個問題導致了任何問題。幸運的是,Postgres會立即失敗,所以唯一的性能影響是應用程序,對數據庫來說沒有什麼意義。

關鍵組件正在捕獲每個type of error,知道哪些情況需要回滾,並且具有exponential backoff for retries。我發現即時重試或靜態睡眠時間會導致進程簡單地反覆死鎖,並導致一點多米諾骨牌效應,這是有道理的。

這是一個完整的邏輯我的系統需要處理每併發問題(僞):

begin transaction (either read committed or serializable) 
while not successful and count < 5 
    try 
     execute sql 
     commit 
    except 
     if error code is '40P01' or '55P03' 
      # Deadlock or lock not available 
      sleep a random time (200 ms to 1 sec) * number of retries 
     else if error code is '40001' or '25P02' 
      # "In failed sql transaction" or serialized transaction failure 
      rollback 
      sleep a random time (200 ms to 1 sec) * number of retries 
      begin transaction 
     else if error message is 'There is no active transaction' 
      sleep a random time (200 ms to 1 sec) * number of retries 
      begin transaction 
    increment count