2012-10-25 161 views
15

我有一個存儲過程,我想確保它不能同時執行。我應該避免使用sp_getAppLock嗎?

我的(多線程)應用程序不通過這個存儲過程基礎表上的所有必要的工作。因此,鎖定表本身是一個不必要的劇烈行動,所以當我發現sp_GetAppLock時,它基本上強制執行臨界區,這聽起來很理想。

我的計劃是包住存儲過程中的事務,並建立與spGetAppLock事務範圍。代碼已成功編寫和測試。

代碼現在已經提出了檢討,我被告知,我不應該調用這個函數。然而,當問一個明顯的問題「爲什麼不?」時,我得到的唯一理由是非常主觀的,任何形式的鎖定都是複雜的。

我不一定買這個,但我想知道是否有任何人有任何客觀爲什麼我應該避免這個結構。就像我說的那樣,根據我的情況,關鍵部分聽起來對我來說是理想的方法。

TIA, 皮特

進一步信息:應用程序坐在與2個線程T1和T2的這個頂部。每個線程正在等待不同的消息M1和M2。涉及的業務邏輯表示,只有在M1和M2都抵達後才能進行處理。存儲過程記錄Mx到達(插入),然後檢查My是否存在(select)。內置鎖定很好,以確保插入順序發生。但是選擇也需要連續發生,我認爲我需要做一些超越內置功能的東西。

爲了清楚起見,我希望「處理」只發生一次。所以我不能讓存儲過程返回誤報或漏報。我很擔心,如果存儲過程非常快速地連續運行兩次,那麼兩個「選擇」都可能返回表示適合執行處理的數據。

+0

通常情況下,sp_GetAppLock是最後的手段,當不應該同時訪問項目(可能導致死鎖)時,如果死鎖是您的問題,請嘗試降低隔離級別(需要進行測試以確保較低的隔離級別可接受)。通常閱讀承諾是最好的。 – Farfarak

+0

您能否確認存儲過程僅由T1和T2調用? – Farfarak

+0

是的,這是正確的。 – PeteH

回答

5

這樣做的過程你不能依賴SQL Server內置的併發控制機制嗎?通常查詢可以被重寫以允許真正的併發。

但如果這個程序確實要執行「獨」,鎖定在第一次訪問表本身是最有可能會是一個很大的速度比使用呼叫sp_GetAppLock。這聽起來像這個程序將被經常調用。如果是這種情況,你應該尋找一種方法,以最小的影響達到目標。


如果表中除M1和M2之外不包含其他行,表鎖仍然是您最好的選擇。

如果你有多個線程發送多封郵件,你可以得到更多的細粒度使用「序列化」的交易水平,並檢查其它消息有沒有你做的插入之前,但在同一事務中。爲防止在這種情況下發生死鎖,請確保您檢查兩條消息,例如:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
BEGIN TRAN; 
SELECT 
@hasM1 = MAX(CASE WHEN msg_type='M1' THEN 1 ELSE 0 END), 
@hasM2 = MAX(CASE WHEN msg_type='M2' THEN 1 ELSE 0 END) 
FROM messages WITH(UPDLOCK) 
WHERE msg_type IN ('M1','M2') 

INSERT ... 

IF(??) EXEC do_other_stuff_and_delete_messages; 
COMMIT 

在IF語句之前(!)COMMIT可以使用插入前收集的信息和插入的信息來決定是否需要額外的處理。

在該處理步驟中確保將其標記那些消息作爲處理或在同一事務中仍然刪除人這將確保您不會處理這些消息兩次。

SERIALIZABLE是唯一允許鎖定不存在的行的事務隔離級別,因此第一個帶有「WITH(UPDLOCK)」的select語句有效地防止在第一次執行仍在運行時插入另一行。

最後,這些是很多事情要注意,可能會出錯。您可能需要查看服務代理。你可以使用三個隊列。一個用於M1型,一個用於M2型。每次消息到達這些隊列中時,都會自動調用一個過程來將令牌插入到第三個隊列中。然後第三個隊列可以激活一個進程來檢查兩個消息是否存在並且工作。這將使整個過程異步,但爲此,將隊列3響應限制爲一次只能執行一次檢查會很容易。

服務代理msdn, 也查看「激活」的自動消息處理。

+0

查看附加到原始問題的註釋以瞭解我正在嘗試執行的操作 – PeteH

+2

'sp_getapplock'會比表鎖更小的影響。因爲它只會影響試圖獲取相同鎖的其他進程(即阻止同一存儲過程的併發調用)。沒有任何其他進程訪問被非法鎖定的表。 –

+1

@MartinSmith如果在整個應用程序中的幾個地方使用表格,你會是對的。但是,我認爲這個程序是唯一的或至少是主要的訪問點。在這種情況下,調用sp_GetAppLock將會明顯更昂貴。 –

0

您可以創建一個表,每個組消息的一個標誌,如果一個線程是首先開始處理這將標誌着標誌作爲處理等等。

爲了確保正確的記錄,一旦阻塞線程中的一個達到其使用方法:

SELECT ... FROM WITH(XLOCK,ROWLOCK,READCOMMITTED) ... WHERE ... 

這個代碼的和平將獨佔鎖在紀錄含義誰第一個得到它擁有的行。 然後你做你的更改和更新標誌,其他線程將獲得更新的值,因爲它將被排他鎖直到第一個線程通知或回滾事務。

對於這個工作,你總是需要從表中的記錄與XLOCK這樣按預期它會工作。

希望這會有所幫助。

排它鎖證明:

USE master 
    GO 

    IF OBJECT_ID('dbo.tblTest') IS NOT NULL 
     DROP TABLE dbo.tblTest 

    CREATE TABLE tblTest (id int PRIMARY KEY) 

    ;WITH cteNumbers AS (
     SELECT 1 N 
     UNION ALL 
     SELECT N + 1 FROM cteNumbers WHERE N<1000 
    ) 
    INSERT INTO 
     tblTest 
    SELECT 
     N 
    FROM 
     cteNumbers 
    OPTION (MAXRECURSION 0) 

    BEGIN TRANSACTION 

    SELECT * FROM dbo.tblTest WITH(XLOCK,ROWLOCK,READCOMMITTED) WHERE id = 1 

    SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID('master') 

    ROLLBACK TRANSACTION 
+5

爲什麼還要用行來創建一個表,只是爲了鎖定某些東西?只需使用'sp_getapplock'。驗證它比解決方案更爲簡單。 –

+0

@Martin Smith從代碼角度看問題角度不正確或不正確? – Farfarak

+0

我刪除了部分評論。 [我正在考慮這個問題](http://sqlblog.com/blogs/paul_white/archive/2010/11/01/read-committed-shared-locks-and-rollbacks.aspx),但在這裏不相關。 –

2

我們使用sp_getapplock所有的時間運行相同的SQL Server存儲過程,由於這樣的事實,我們支持一些舊的應用程序已重新使用SQL後端,並且SQL Server鎖定模型與我們的應用程序邏輯不完全匹配。

我們傾向於採用'悲觀'鎖定模式,在允許用戶編輯它之前鎖定實體,並在讀取數據時使用(NOLOCK)提示以繞過實際表上的本機鎖的任何阻止。 sp_getapplock是一個很好的匹配。我們還使用它來在大型多用戶系統中實施關鍵路徑。你必須對你所說的鎖具進行系統的分析。

我們發現通過這條路線沒有大量用戶/鎖的性能問題,所以我沒有理由認爲它不適合你。請注意,如果您有放置相同命名鎖的進程,但不一定按相同順序執行,則可能會發生阻塞和死鎖。

相關問題