2014-01-05 72 views
1

我想鎖定一行,直到他無限期地使用該行,並且他必須在完成時解鎖。所以其他用戶將無法爲自己鎖定此行。有可能在數據庫級別上做?PostgreSQL無限期地鎖定行

回答

1

你可以用一個長壽命的交易來做到這一點,但是會有性能問題。這聽起來更像是樂觀併發控制的工作。

可以只是打開一個交易,並做一個SELECT 1 FROM mytable WHERE clause to match row FOR UPDATE;。然後保持交易開放直到完成。問題在於它可能導致真空問題,導致表和索引膨脹,其中表被填充刪除的數據並且索引填滿指向過時塊的條目。

使用advisory lock會更好。您仍然必須保持連接處於鎖定狀態,但它不必保持閒置閒置事務,所以影響要小得多。但是,希望更新行的事務必須顯式檢查是否存在衝突的諮詢鎖,否則它們可以像鎖未鎖定一樣繼續。這種方法對大量表格(由於諮詢鎖定名稱空間有限)或大量併發鎖定(由於連接數量)也很差。

如果您無法確保您的客戶端應用程序始終明確獲取諮詢鎖,則可以使用觸發器來檢查諮詢鎖並等待它。但是,這可能會造成死鎖問題。

因此,最好的方法可能是記錄用戶ID的locked_by字段和記錄鎖定時間的locked_time字段。在應用程序級別和/或觸發器上執行。要處理併發嘗試獲取鎖定,您可以使用optimistic concurrency control技術,其中UPDATE上的WHERE子句設置locked_bylocked_time將不匹配,如果其他人首先到達那裏,則rowcount將爲零,並且您將知道您丟失了比賽的鎖定,必須重新檢查。 WHERE條款通常測試locked_bylocked_time。所以,你會寫類似:

UPDATE t 
SET locked_by = 'me' AND locked_time = current_timestamp 
WHERE locked_by IS NULL AND locked_time IS NULL 
AND id = [ID of row to update]; 

(這是一個簡化的樂觀鎖定模式,抓住一個鎖,在這裏你如果別人跳了做了一個整個事務不介意如果你想更嚴格的順序。 ,您使用行版本列或檢查last_modified列沒有更改。)

+0

看起來像查詢更新所有行,其中「locked_by IS NULL AND locked_time IS NULL」,但如何更新只有一行?我認爲可能是應用程序正在下降並且行保持鎖定狀態,但是當用戶重新啓動程序時,lock_by ='me'所在的行已經存在,因此他需要先將它取出。 PS抱歉我的英語 – Miles

+0

@Miles是的,而是。應該包含感興趣的ID。固定。 –

+0

@Miles至於應用程序失敗,在「無限期鎖定」的同時,您不能輕易「在崩潰時自動解鎖」。您可能不得不依賴於PostgreSQL的開放式TCP連接,無論是建議性鎖定還是長時間運行的tx(但是如果數據庫處於大量寫入負載下,長時間運行的tx真的很糟糕)。 –