以gbn先前在这些问题中提到的主题为基础 Q1,Q2,Q3,Q4,关于使用同义词和重新创建同义词以保持同义词指向实时数据,目前尚不清楚我如何使用
来防止“竞争条件”“事务模式下BEGIN TRAN之后的sp_getapplock并根据需要陷阱/处理返回状态。”
sp_getapplock的MSDN文档对我来说有点神秘。例如,resource_name
可以是任何组成的字符串吗?但更重要的是:如果我运行包含嵌套过程的单个过程,第一步是构建表,如果这些成功,那么下一个主要步骤是DROP
和CREATE
现有的同义词,我将如何正确实现sp_getapplock?
CREATE PROCEDURE [dbo].[some_old_proc]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE @nested_build_success varchar(3) = 'No'
DECLARE @starttrancount int
BEGIN TRY
SET @starttrancount = @@TRANCOUNT
IF @starttrancount = 0
BEGIN TRANSACTION
-- fill the tables that the synonyms don't point to yet...
EXEC dbo.nested_proc_1
EXEC dbo.nested_proc_2
EXEC dbo.nested_proc_3
EXEC dbo.nested_proc_4
EXEC dbo.nested_proc_5
IF @starttrancount = 0
BEGIN
COMMIT TRANSACTION
SET @nested_build_success = 'Yes'
END
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION
-- RAISERROR... log error event
END CATCH
IF @nested_build_success = 'Yes'
BEGIN TRAN
-- simple talk article
-- http://www.simple-talk.com/sql/t-sql-programming/developing-modifications-that-survive-concurrency/
DECLARE @ret INT -- does it matter what the resource_name is?
EXEC @ret = sp_getapplock @Resource = 'DoesNameMatterHere', @LockMode = 'Exclusive';
IF @ret < 0
BEGIN
-- log error message?
END
ELSE
BEGIN
-- call the proc that a does a DROP and CREATE of the relevant synonyms
-- so the synonyms point at a different set of tables...
EXEC dbo.change_the_synonyms
END
COMMIT TRAN
也许存在一种不同的更好的方法来避免竞争条件而不是使用sp_getapplock,或者我正在尝试做的一个很好的例子可用?
答案 0 :(得分:2)
如果我正确理解您的问题,准备步骤和同义词设置必须在单个事务中,而不是两个单独的事务。以下是基于Exception handling and nested transactions模板的示例:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
EXEC sp_getapplock 'usp_my_procedure_name',
'Exclusive',
'TRANSACTION';
EXEC dbo.nested_proc_1;
EXEC dbo.nested_proc_2;
EXEC dbo.nested_proc_3;
EXEC dbo.nested_proc_4;
EXEC dbo.nested_proc_5;
EXEC dbo.change_the_synonyms;
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(),
@message = ERROR_MESSAGE(),
@xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s',
16, 1, @error, @message) ;
end catch
end
这将以原子方式完成所有工作。它将使用applock来序列化访问,这样就不会有两个程序同时执行这项工作。如果出现错误,工作将完全回滚,或者在调用者已经有事务的情况下,它将工作回滚到过程条目的一致状态,而不回滚调用者(这在批量处理)。 XACT_ABORT在部署脚本中有用,但在我的书中将XACT_ABORT与TRY / CATCH混合是一个很大的禁忌。