CAVEAT:这是遗留代码,我们知道我们应该使用一个实际的SQL序列,这里的目标是理解为什么一个方法从另一个方法提供不同的结果,因为我们不能只是动态更改为序列这一次。
问题:我们使用单值表作为序列对象(在2012年之前创建)来生成和返回需要保证唯一的递增数字。存储过程可以正常工作但是会产生高CPU并导致高负载下的严重阻塞;修复看起来好像应该有效但不行。使用新代码可以缓解CPU和阻塞,但我们似乎无法阻止脏读,这会导致重复。
以下是该表的外观:
if OBJECT_ID('dbo.Sequence') is not null
drop table dbo.Sequence;
create table dbo.Sequence (number int Primary Key);
insert into dbo.Sequence values (1);
GO
此存储过程使用可序列化锁定并获得准确结果,但由于阻塞而无法正常运行:
CREATE OR ALTER PROC dbo.usp_GetSequence_Serializable @AddSomeNumber INT = 1 AS
BEGIN
declare @return int; --value to be returned and declared within sproc
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
Select @return = number from dbo.Sequence with (updlock) --get initial value plus one
Update dbo.Sequence set number = @return + @AddSomeNumber --increment it
COMMIT TRANSACTION
Select @return + @AddSomeNumber as nextid
END
GO
这是我们更快的版本,可以避免序列化,但它会不时地重复获取重复值:
CREATE OR ALTER PROC dbo.usp_GetSequence_DefaultIsolationLevel @AddSomeNumber INT = 1 AS
BEGIN
declare @return int; --value to be returned and declared within sproc
update dbo.Sequence set @return = number = number + @AddSomeNumber --we tried interchanging various locking hints with no change in results (likely due to an exclusive being taken already)
Select @return as nextid
END
GO
我们可以做些什么来获得第二个(非序列化)proc的更快性能,同时避免重复生成的ID?
答案 0 :(得分:0)
在较大的数据库中,当您无法避免代码中的死锁时,将会出现时间。数据库将不始终展望所有事情以避免争用。在这些罕见且特殊的情况下,您可以使用SQL Server本身使用的高性能锁定 - > SP_GETAPPLOCK().您可以通过这种方式构建代码,以便对存储过程中的关键部分进行独占访问,并对其进行序列化访问。当所有传统的锁定提示都失败时,SP_GETAPPLOCK
绝对不是坏事。
DECLARE @MyTimeoutMiliseconds INT =5000--Wait only five seconds max then timeout
BEGIN TRAN
EXEC @LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',@MyTimeoutMiliseconds
IF(@LockRequestResult>=0)BEGIN
/*
DO YOUR CRITICAL READS AND WRITES HERE
You may prefer try finally to release
*/
COMMIT TRAN -- <--Releases the lock!
END ELSE
ROLLBACK TRAN --<--Releases the lock!