以下sproc尝试将一行插入表中并生成随机ID,该ID用于相应表上的PK。在catch块中处理与随机生成的ID的冲突,其中重试/再次调用该过程。现在,这需要很长时间并导致死锁,因为锁会保持很长一段时间。有没有办法在重试之前立即释放死锁,以便在其他线程成功锁定PK索引时有一个短窗口?
CREATE PROCEDURE addPerson
(
@FirstName nvarchar(100),
@LastName nvarchar(100)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @PersonId int
-- generate random PersonId
-- this sproc can generate ids that already exist in the table
EXEC generateRandomPersonId @PersonId=@PersonId OUTPUT
BEGIN TRY
INSERT INTO [dbo].[Persons]
(
PersonId,FirstName,LastName
)
VALUES
(
@PersonId,@FirstName,@LastName
)
BEGIN CATCH
--
-- HOW TO RELEASE LOCKS HERE that are still held
-- for the previous INSERT statement?
--
DECLARE @ErrorNumber int, @ErrorMessage nvarchar(2048)
SELECT @ErrorNumber=ERROR_NUMBER(),
@ErrorMessage=ERROR_MESSAGE()
-- if a race condition happened and
-- PersonId happened to be picked already, retry all over again
IF (@ErrorNumber = 2601 OR @ErrorNumber = 2627 AND CHARINDEX(N'PK_Persons_PersonId', @ErrorMessage) > 0)
BEGIN
--
-- RETRYING HERE participates in a high possibility and
-- occurrence of deadlocks
--
EXEC addPerson @FirstName,@LastName
END
ELSE
-- some other error, rethrow it
EXEC rethrowError
END
END CATCH
END
GO
答案 0 :(得分:2)
进程不会阻塞自己的锁。由于对存储过程的调用在同一进程中运行,因此第二个insert
无法等待第一个insert
的锁定。
你能发布一个死锁图吗?这显示了有关阻止过程的大量信息。
作为一个快速解决方案,你可以在一个循环中搜索一个免费的ID,这可以避免大多数(但不是全部)可能的冲突:
while 1=1
begin
EXEC generateRandomPersonId @PersonId=@PersonId OUTPUT
if not exists (select * from Persons where PersonId = @PersonID)
break
end
答案 1 :(得分:1)
首先,我希望这个程序只适用于学术环境,而不适用于商业产品或真实生产环境。
这是一种方法:
CREATE PROCEDURE addPerson
(
@FirstName nvarchar(100),
@LastName nvarchar(100)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @doed bit
set @doed = 0
DECLARE @PersonId int
WHILE @doed = 0
BEGIN
-- generate random PersonId
-- this sproc can generate ids that already exist in the table
EXEC generateRandomPersonId @PersonId=@PersonId OUTPUT
BEGIN TRANSACTION ExceptionHandling
BEGIN TRY
INSERT INTO [dbo].[Persons]
(
PersonId,FirstName,LastName
)
VALUES
(
@PersonId,@FirstName,@LastName
)
COMMIT TRANSACTION ExceptionHandling
BEGIN CATCH
ROLLBACK TRANSACTION ExceptionHandling
--
-- HOW TO RELEASE LOCKS HERE that are still held
-- for the previous INSERT statement?
--
DECLARE @ErrorNumber int, @ErrorMessage nvarchar(2048)
SELECT @ErrorNumber=ERROR_NUMBER(),
@ErrorMessage=ERROR_MESSAGE()
-- if a race condition happened and
-- PersonId happened to be picked already, retry all over again
IF !(@ErrorNumber = 2601 OR @ErrorNumber = 2627 AND CHARINDEX(N'PK_Persons_PersonId', @ErrorMessage) > 0)
BEGIN
--
-- RETRYING HERE participates in a high possibility and
-- occurrence of deadlocks
set @doed = 0
END
ELSE
-- some other error, rethrow it
set @doed = 1
EXEC rethrowError
END
END CATCH
END --end while
END
GO
免责声明:未经测试