2015-10-14 61 views
4

在我的Rails應用4,我有這樣的查詢到一個Postgres 9.4數據庫:諮詢鎖或NOWAIT以避免等待鎖定的行?

@chosen_opportunity = Opportunity.find_by_sql(
    " UPDATE \"opportunities\" s 
    SET opportunity_available = false 
    FROM (
      SELECT \"opportunities\".* 
      FROM \"opportunities\" 
      WHERE (deal_id = #{@deal.id} 
      AND opportunity_available = true 
      AND pg_try_advisory_xact_lock(id)) 
      LIMIT 1 
      FOR UPDATE 
     ) sub 
    WHERE  s.id = sub.id 
    RETURNING sub.prize_id, sub.id" 
) 

很受this related answer on dba.SE啓發。

我只是希望我的查詢來查找和更新的第一個(隨機,與LIMIT)排在那裏available = true和其更新爲available = false,我需要鎖定該行,而這樣做,但沒有使等待發行的新要求因爲有很多併發呼叫將使用此查詢。我也看到NOWAIT選項FOR UPDATE。我不知道我的理解使用pg_try_advisory_xact_lock()NOWAIT選項之間的差別,他們似乎對我達到同樣的目標:

  • NOWAIT's goal

    要防止等待其他操作要提交的事務,請使用NOWAIT選項。

  • pg_try_advisory_xact_lock's goal
    不是等待一個事務解除鎖定,仍然能夠做其他事務,並只經營則下一個更新選擇「尚未鎖定」行。

哪一個更適合我的需要?

回答

4

FOR UPDATE NOWAIT 僅僅是一個好主意,如果你堅持要鎖定特定行,這是你所需要的。你只想任何排位賽,可用(解鎖)行。最重要的區別是(quoting the manual for Postgres 9.4):

隨着NOWAIT,該語句報告錯誤,而不是等待,如果選擇的行不能立即鎖定。

相同的查詢很可能會嘗試鎖定相同的任意選擇。 FOR UPDATE NOWAIT只會出現一個異常(除非您捕獲錯誤,否則它將回滾整個事務)並且必須重試。

在dba.SE我的參考答案該解決方案採用的簡單的FOR UPDATE結合pg_try_advisory_lock()組合:

pg_try_advisory_lock類似於爲P g_advisory_lock,除了 功能不會等待鎖成爲可用。它會 立即獲得鎖並返回true,或者如果 無法立即獲取鎖,則返回false。

所以你最好的選擇是......第三個選擇:在Postgres的9.5(目前是測試版),它實現了無需額外的函數調用相同的行爲,新FOR UPDATE SKIP LOCKED

The manual for Postgres 9.5比較這兩個選項,解釋差異多一些:

要防止等待其他事務 提交,使用NOWAITSKIP LOCKED選項操作。使用NOWAIT時,如果所選行 不能立即鎖定,則 語句會報告錯誤而不是等待。通過SKIP LOCKED,可以跳過 無法立即鎖定的任何選定行。

,同時等待新版本,你下一個最好的選擇是結合使用pg_try_advisory_xact_lock(id)FOR UPDATE像展示在參考答案:

(也執行FOR UPDATE SKIP LOCKED。)

旁白

嚴格來說,你會得到任意的,而不是真正的隨機選擇。這可能是一個重要的區別。
查詢的審計版本是my answer to your other question

+0

非常感謝您的詳細解答。我會盡力實施下一個星期一。是的,你是正確的更新跳過鎖定是最合適的。我可能會等待它的發佈,並在此期間使用pg顧問鎖定。因爲我的排在這個階段已經隨機分佈了,所以如果我沒有得到任意選擇,這並不重要。 – Mathieu

+0

@Mathieu:「任意」也很重要,因爲它意味着Postgres通常會爲相同的查詢*任意*選擇*相同的*行,這使得鎖爭論比真正的隨機選擇更大。 –

+0

你是什麼意思'它會一直選擇同一行'?如果你願意,我的應用程序是一種彩票。當玩家檢查'機票=機會'時,我會將其更新爲available = false當玩家的下一張照片出現時,它會使用我們關注的查詢並選擇任何行,其中opportunity_available =真正。所以如果查詢總是選擇同一行,因爲一旦玩家打開它就會被更新爲不可用,我不認爲這個任意的方面會影響我。你做? postgresql菜鳥 – Mathieu