2010-02-25 83 views
4

我有一個服務器應用程序和一個數據庫。服務器的多個實例可以同時運行,但所有數據都來自同一個數據庫(在某些服務器上是postgresql,在其他情況下是ms sql服務器)。數據庫記錄鎖定

在我的應用程序中,有一個過程需要幾個小時才能完成。我需要確保這個過程一次只能執行一次。如果一臺服務器正在處理,則其他服務器實例不能處理,直到第一個服務器實例完成。

該過程依賴於一個表(我們稱之爲'ProcessTable')。我所做的是,在任何服務器啓動長達一小時的過程之前,我在ProcessTable中設置一個布爾標誌,表示此記錄已被「鎖定」並正在處理中(並未處理/鎖定此表中的所有記錄,所以我需要專門標記過程所需的每條記錄)。因此,當下一個服務器實例出現而前一個實例仍在處理時,它會看到布爾標誌並引發異常。

問題是,兩個服務器實例可能幾乎同時被激活,並且當兩者都檢查ProcessTable時,可能沒有設置任何標誌,但兩個服務器實際上都在「設置」標誌,但由於事務尚未提交任一進程,因此這兩個進程都不會看到其他進程完成的鎖定。這是因爲鎖定機制本身可能需要幾秒鐘的時間,所以有兩個服務器仍然可以同時處理的機會窗口。

看來,我需要的是我的'設置'表中應該存儲一個名爲'LockInProgress'的布爾標誌的單個記錄。因此,在即使服務器可以鎖定ProcessTable中所需的記錄之前,首先必須通過檢查Settings表中的'LockInProgress'列來確保它具有完全鎖定權限。

所以我的問題是,如何防止兩個服務器同時修改設置表中的LockInProgress列......或者我以錯誤的方式解決這個問題?

請注意,我需要支持postgresql和MS SQL服務器,因爲有些服務器使用一個數據庫,而有些服務器使用另一個。

在此先感謝...

回答

0

作出這樣的手了鎖的存儲過程,並在「序列化」隔離運行它。這將保證在任何特定時間只有一個進程可以獲取資源。

請注意,這意味着試圖鎖定第二個進程將阻塞,直到第一個進程釋放它。另外,如果您必須以這種方式獲取多個鎖,請確保該過程的設計可確保以相同的順序獲取並釋放鎖。這將避免兩個進程在等待彼​​此釋放鎖時保存資源的死鎖情況。

除非你不能處理你的其他進程阻塞這可能會比實現'測試和設置'的語義更容易實現和更強大。

+0

這是一個更好的主意,然後從Pradeep的答案,如果是這樣,爲什麼?看起來,這可能會起作用,但它似乎也有點複雜(可能更少一些「跨平臺」)......我可能是錯的...... – user85116 2010-02-25 19:01:31

+0

您可能需要爲每個支持的平臺編寫不同的sproc ,但編寫某種插件機制以支持少數特定於平臺的操作並不困難。這種方法的關鍵在於它使用本地鎖定機制和語義(即一個進程被阻塞直到鎖定變爲空閒)。 – ConcernedOfTunbridgeWells 2010-02-26 07:29:16

1

如何先獲取記錄上的鎖定,然後更新記錄以顯示「鎖定」。這將避免第二個實例成功獲得鎖,從而更新記錄失敗。

重點是確保鎖定和更新作爲一個原子步驟。

+0

你可以擴大一點嗎?我在前端使用jdbc ...是java.sql.Connection.TRANSACTION_SERIALIZABLE我在這裏之後? – user85116 2010-02-25 18:59:39

+0

我已經在Oracle PL/SQL過程中做了幾年。首先你在行上獲得一個鎖。成功鎖定時,檢查該值是否已被鎖定,否則更新爲「鎖定」並釋放鎖定。這將確保第二個線程只有在第一個線程釋放它之後纔會獲得鎖定,並且現在將被「鎖定」。 – Pradeep 2010-02-26 22:29:30

+0

引用此: 「事務隔離級別的一個示例是TRANSACTION_READ_COMMITTED,它不允許直到它被提交之後訪問該值。換句話說,如果事務隔離級別設置爲TRANSACTION_READ_COMMITTED,則DBMS不會允許發生髒讀,接口Connection包含五個值,它們表示您可以在JDBC中使用的事務隔離級別。「 來自鏈接:http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Statement.html – Pradeep 2010-02-26 22:47:54

0

我一直在想這個,我認爲這是最簡單的做事方式;我只是執行命令是這樣的:

update settings set settingsValue = '333' where settingsKey = 'ProcessLock' and settingsValue = '0' 

「333」將是一個獨特的價值,每個服務器進程獲得(基於日期/時間,服務器名稱+隨機值等)。

如果沒有其他進程鎖定表,那麼settingsValue將= = 0,並且該語句將調整settingsValue。

如果另一個進程已經鎖定了表,那麼該語句就變成了no-op,並且沒有得到修改。

然後我立即提交交易。

最後,我查詢了settingsValue的表,如果它是正確的值,那麼我們的鎖成功了,我們繼續,否則拋出異常等等。當我們完成鎖定時,我們重置值減少到0.

由於我使用的是SERIALIZATION事務模式,所以我看不到任何問題......請糾正我,如果我錯了。