如何让 SQL Server 2019 假装存储过程正在进行中?
How can I make SQL Server 2019 pretend that a stored proc is in progress?
我有一个存储过程,可以从实时生产数据库中刷新报告数据库中的多个表。
我运行:
EXEC Reporting.dbo.RefreshTemps
这个存储过程非常简单,它所做的只是运行分类几个表,然后运行插入它们以获得刷新的数据快照。大约需要 10 分钟才能完成,因为它们是非常厚实的桌子。
但是,许多人、进程或其他程序可能希望在白天使用此程序。完全合理的是,用户 A 可能会启动进程 运行ning,然后用户 B 在用户 A 的 运行 完成之前启动相同的进程 运行ning。
这意味着一旦用户 A 的过程完成,用户 B 的过程将处于 t运行更新和刷新相同表的过程中。这会很糟糕。
理想情况下,SQL 不会 运行 用户 B 的 proc,等待用户 A 的 运行 完成,用户 A 和用户 B 将同时公开更新的表时间.
有什么方法可以让 SQL 误以为用户 B 的进程正常 运行,但它实际上在等待用户 A 的 运行 完成?
这里最简单的做法是使用 SQL 服务器代理作业。您可以使用 sp_start_job 开始一项工作,并且任何时候都只能 运行 一份工作副本。
就像 Dan Guzman 建议的那样,您也可以使用 sp_getapplock。但是您可能想要使用会话锁,否则您的长期 运行ning 作业将不得不在事务中 运行。会话锁可能很棘手。很容易得到一个阻塞的进程。无论如何,它可能看起来像这样:
create or alter procedure ThereCanBeOnlyOne @sql nvarchar(max)
as
begin
--hash the sql batch to generate the app lock name
declare @lockname nvarchar(255) = concat('lock_',convert(nvarchar(255), hashbytes('MD5', @sql) , 1));
print @lockname
declare @result int
--request an applock with an immediate timeout
exec @result = sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session',@LockTimeout=0
if @result = -1 --lock timeout procedure is already running
begin
--wait for other session to finish and return
exec sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session';
exec sp_releaseapplock @Resource=@lockname, @LockOwner='Session';
print 'batch completed in another session';
return 0;
end
begin try
--actually run the batch
exec (@sql);
exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
print 'batch completed in this session';
return 0;
end try
begin catch
if (APPLOCK_MODE('public', @lockname, 'Session') = 'Exclusive')
exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
throw;
end catch
end
我有一个存储过程,可以从实时生产数据库中刷新报告数据库中的多个表。
我运行:
EXEC Reporting.dbo.RefreshTemps
这个存储过程非常简单,它所做的只是运行分类几个表,然后运行插入它们以获得刷新的数据快照。大约需要 10 分钟才能完成,因为它们是非常厚实的桌子。
但是,许多人、进程或其他程序可能希望在白天使用此程序。完全合理的是,用户 A 可能会启动进程 运行ning,然后用户 B 在用户 A 的 运行 完成之前启动相同的进程 运行ning。
这意味着一旦用户 A 的过程完成,用户 B 的过程将处于 t运行更新和刷新相同表的过程中。这会很糟糕。
理想情况下,SQL 不会 运行 用户 B 的 proc,等待用户 A 的 运行 完成,用户 A 和用户 B 将同时公开更新的表时间.
有什么方法可以让 SQL 误以为用户 B 的进程正常 运行,但它实际上在等待用户 A 的 运行 完成?
这里最简单的做法是使用 SQL 服务器代理作业。您可以使用 sp_start_job 开始一项工作,并且任何时候都只能 运行 一份工作副本。
就像 Dan Guzman 建议的那样,您也可以使用 sp_getapplock。但是您可能想要使用会话锁,否则您的长期 运行ning 作业将不得不在事务中 运行。会话锁可能很棘手。很容易得到一个阻塞的进程。无论如何,它可能看起来像这样:
create or alter procedure ThereCanBeOnlyOne @sql nvarchar(max)
as
begin
--hash the sql batch to generate the app lock name
declare @lockname nvarchar(255) = concat('lock_',convert(nvarchar(255), hashbytes('MD5', @sql) , 1));
print @lockname
declare @result int
--request an applock with an immediate timeout
exec @result = sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session',@LockTimeout=0
if @result = -1 --lock timeout procedure is already running
begin
--wait for other session to finish and return
exec sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session';
exec sp_releaseapplock @Resource=@lockname, @LockOwner='Session';
print 'batch completed in another session';
return 0;
end
begin try
--actually run the batch
exec (@sql);
exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
print 'batch completed in this session';
return 0;
end try
begin catch
if (APPLOCK_MODE('public', @lockname, 'Session') = 'Exclusive')
exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
throw;
end catch
end