1

我的 SQL 语句遇到死锁,我想在其中选择一个 ID(如果存在),否则插入然后选择它。正如这里所建议的,我正在使用双重检查锁定来防止锁定开销。

显然我这样做是为了支持并发插入,并且我正在运行多个线程。我的 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 MunicipalityID=@MunicipalityId AND ZipCode=@ZipCode
IF @id IS NULL
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
        SELECT @id = ZipCityId FROM ZipCity WHERE MunicipalityID=@MunicipalityId AND ZipCode=@ZipCode
        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

更新

这可以通过在事务内的 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;
4

2 回答 2

2
    SELECT @id = ZipCityId 
    FROM ZipCity 
    WHERE MunicipalityID=@MunicipalityId 
      AND ZipCode=@ZipCode

在这里,您的选择是获取 S 锁。这可能发生在多个线程上。稍后,插入尝试 X 锁定,这是一个死锁。

立即获取 X 锁:

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

在这里,ROWLOCK, HOLDLOCK不是严格要求的,但模式XLOCK, ROWLOCK, HOLDLOCK非常标准,我尝试在任何地方都遵循它以保持一致性。

顺便说一句,您可能想切换到MERGE语句。我认为它会自动获取 U 锁,因此不需要锁定提示。不过,不确定。无论如何,这将是代码改进和性能改进。

于 2013-11-28T10:55:24.007 回答
0

好吧,很难重现您的场景,但它看起来确实很有趣。

尝试使用 ROWLOCK 即做这样的事情

SELECT @id = ZipCityId FROM ZipCity WITH(ROWLOCK) WHERE MunicipalityID=@MunicipalityId AND ZipCode=@ZipCode

看看它是否有帮助(我希望它有帮助)。

此外,您可能想查看这篇文章,看看它是否与您的场景相关。在我看来是。

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

于 2013-11-28T10:54:16.190 回答