2013-11-28 103 views
1

我遇到從我的SQL語句中遇到死鎖,如果它存在,我想選擇它,否則插入並選擇它。我使用雙重檢查鎖定來防止鎖定開銷,建議here在SQL中使用雙重檢查鎖定的死鎖

很明顯,我這樣做是爲了支持併發插入,而且我正在運行多個線程。我的SQL技巧非常低,所以我可能錯過了關於鎖定的基本知識?這裏是我的方法:

CREATE PROCEDURE InsertAndOrSelectZipCity 
@PostalDistrict nvarchar(25), 
@CityName nvarchar(34), 
@MunicipalityId smallint, 
@ZipCode smallint 
AS 

DECLARE @id AS INT 
SELECT @id = ZipCityId FROM ZipCity (NOLOCK) WHERE [email protected] AND [email protected] 
IF @id IS NULL 
BEGIN 
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
    BEGIN TRANSACTION 
     SELECT @id = ZipCityId FROM ZipCity WHERE [email protected] AND [email protected] 
     IF @id IS NULL 
     BEGIN 
      INSERT INTO ZipCity (PostalDistrict, CityName, MunicipalityId, ZipCode) VALUES (@PostalDistrict, @CityName, @MunicipalityId, @ZipCode) 
      SELECT @id = SCOPE_IDENTITY() 
     END 
    COMMIT TRANSACTION 
END 
SELECT @id 

UPDATE

這是通過使用在TRANSATION內SELECT語句中的適當的鎖(XLOCK,ROWLOCK,HOLDLOCK)固定。

以下是使用MERGE語句來代替,沒有事務需要編寫的程序:

DECLARE @id as INT 

MERGE INTO ZipCity WITH (TABLOCK) AS Target 
USING (SELECT @PostalDistrict, @CityName, @MunicipalityId, @ZipCode) AS Source (PostalDistrict, CityName, MunicipalityId, ZipCode) 
ON Target.MunicipalityId = Source.MunicipalityId AND Target.ZipCode = Source.ZipCode 
WHEN MATCHED THEN 
    UPDATE SET @id = Target.ZipCityId 
WHEN NOT MATCHED THEN 
    INSERT (PostalDistrict, CityName, MunicipalityId, ZipCode) VALUES (@PostalDistrict, @CityName, @MunicipalityId, @ZipCode) 
OUTPUT INSERTED.ZipCityId; 

回答

2
SELECT @id = ZipCityId 
    FROM ZipCity 
    WHERE [email protected] 
     AND [email protected] 

這裏您的選擇是獲取S鎖。這可能發生在多個線程中。後來,插入嘗試X鎖,這是一個死鎖。

採集X鎖直線距離:

SELECT @id = ZipCityId 
    FROM ZipCity WITH (XLOCK, ROWLOCK, HOLDLOCK) ... 

這裏,ROWLOCK, HOLDLOCK並不嚴格要求,但該模式XLOCK, ROWLOCK, HOLDLOCK是非常標準的,我嘗試遵循它無處不在的一致性。

順便說一句,你可能想切換到MERGE聲明。我認爲它會自動獲得U鎖,因此不需要鎖定提示。但是不確定。無論如何,這將是一個代碼改進以及性能改進。

+0

這樣做 - 謝謝!對於MERGE建議,如果我的數據是靜態的,那麼不得不使用UPDATE語句的開銷嗎?例如。每個ZipCity行總是相同的。 – Jeppebm

+1

MERGE可以包含任何您想要的子句的組合。你可以只有一個不匹配然後插入,沒有別的。合併是所有其他DML語句的超集。 – usr

+1

輝煌。使用MERGE後,我會不得不選擇(NOLOCK)後ZipCityID,以實現相同的輸出和功能? – Jeppebm

0

嗯,這是難以重現您的方案,但它看起來有趣。

嘗試使用ROWLOCK即做這樣的事情

SELECT @id = ZipCityId FROM ZipCity WITH(ROWLOCK) WHERE [email protected] AND [email protected] 

,並看看是否有幫助(我希望它)。

此外,如果您的方案與您的方案相關,您可能需要查看本文並查看 。對我來說似乎是這樣。

http://support.microsoft.com/kb/323630

+0

仍然只使用行鎖死鎖。 usr的回答使用(XLOCK,ROWLOCK,HOLDLOCK)似乎工作。謝謝你的鏈接。 – Jeppebm

+0

好的,你解決了你的問題。 –