SQL Service Broker内部激活问题
我为两个存储过程设置了内部激活。一个用于插入一条或多条记录,另一个用于更新同一表中的一条或多条记录。所以,我有两个启动器,两个目标队列。 到目前为止,它在开发中运行良好,但我想知道当我们将它转移到prod时,可能会遇到哪些类型的问题,在prod中经常调用这两个存储过程。我们已经遇到了由这两个存储过程引起的死锁问题。异步执行是我使用此实现的主要目标 问题:SQL Service Broker内部激活问题,sql,sql-server,internal,service-broker,activation,Sql,Sql Server,Internal,Service Broker,Activation,我为两个存储过程设置了内部激活。一个用于插入一条或多条记录,另一个用于更新同一表中的一条或多条记录。所以,我有两个启动器,两个目标队列。 到目前为止,它在开发中运行良好,但我想知道当我们将它转移到prod时,可能会遇到哪些类型的问题,在prod中经常调用这两个存储过程。我们已经遇到了由这两个存储过程引起的死锁问题。异步执行是我使用此实现的主要目标 问题: 是否有一种方法可以对两个存储过程使用一个目标队列来防止死锁 我能做些什么使它更可靠吗?像一个执行错误一样,不应该停止传入的请求 排队 提高可伸
CREATE QUEUE [RecordAddUsersQueue];
CREATE SERVICE [RecordAddUsersService] ON QUEUE [RecordAddUsersQueue];
ALTER QUEUE [AddUsersQueue] WITH ACTIVATION
( STATUS = ON,
MAX_QUEUE_READERS = 1, --or 10?
PROCEDURE_NAME = usp_AddInstanceUsers,
EXECUTE AS OWNER);
CREATE PROCEDURE [dbo].[usp_AddInstanceUsers] @UsersXml xml
AS
BEGIN
DECLARE @Handle uniqueidentifier;
BEGIN DIALOG CONVERSATION @Handle
FROM SERVICE [RecordAddUsersService]
TO SERVICE 'AddUsersService'
ON CONTRACT [AddUsersContract]
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION @Handle
MESSAGE TYPE [AddUsersXML] (@UsersXml);
END
GO
CREATE PROCEDURE [dbo].[usp_SB_AddInstanceUsers]
AS
BEGIN
DECLARE @Handle uniqueidentifier;
DECLARE @MessageType sysname;
DECLARE @UsersXML xml;
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION;
WAITFOR
(RECEIVE TOP (1)
@Handle = conversation_handle,
@MessageType = message_type_name,
@UsersXML = message_body
FROM [AddUsersQueue]), TIMEOUT 5000;
IF (@@ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION;
BREAK;
END
IF (@MessageType = 'ReqAddUsersXML')
BEGIN
--<INSERT>....
DECLARE @ReplyMsg nvarchar(100);
SELECT
@ReplyMsg = N'<ReplyMsg>Message for AddUsers Initiator service.</ReplyMsg>';
SEND ON CONVERSATION @Handle
MESSAGE TYPE [RepAddUsersXML] (@ReplyMsg);
END
ELSE
IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
BEGIN
END CONVERSATION @Handle;
END
ELSE
IF @MessageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
BEGIN
END CONVERSATION @Handle;
END
COMMIT TRANSACTION;
END
END
GO
创建队列[RecordAddUsersQueue];
在队列[RecordAddUsersQueue]上创建服务[RecordAddUsersService];
通过激活更改队列[AddUsersQueue]
(状态=开,
最大队列读取器=1,--或10?
过程\u NAME=usp\u AddInstanceUsers,
作为业主执行);
创建过程[dbo].[usp\U AddInstanceUsers]@UsersXml
作为
开始
声明@Handle唯一标识符;
开始对话@Handle
来自服务[RecordAddUsersService]
为“AddUsersService”提供服务
根据合同[AddUsersContract]
加密=关闭时;
发送会话@Handle
消息类型[AddUsersXML](@UsersXml);
结束
去
创建过程[dbo]。[usp\U SB\U AddInstanceUsers]
作为
开始
声明@Handle唯一标识符;
声明@MessageType sysname;
声明@UsersXML;
而(1=1)
开始
开始交易;
等待
(接收顶部(1)
@Handle=conversation\u Handle,
@MessageType=消息类型名称,
@UsersXML=消息体
从[AddUsersQueue]),超时5000;
如果(@@ROWCOUNT=0)
开始
回滚事务;
打破
结束
IF(@MessageType='ReqAddUsersXML')
开始
--....
声明@ReplyMsg nvarchar(100);
挑选
@ReplyMsg=N'AddUsers启动器服务的消息';
发送会话@Handle
消息类型[RepAddUsersXML](@ReplyMsg);
结束
其他的
如果@MessageType=N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
开始
结束对话@Handle;
结束
其他的
如果@MessageType=N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
开始
结束对话@Handle;
结束
提交事务;
结束
结束
去
谢谢,
库齐
是否有一种方法可以对两个存储过程使用一个目标队列来防止死锁
你可以,你应该。没有理由有两个目标服务/队列/过程。向同一服务发送两种不同的消息类型,用于所需的两种操作。然后,激活的过程应根据消息类型执行添加逻辑或更新逻辑
我能做些什么使它更可靠吗?像一个执行错误不应该停止对队列的传入请求
SSB激活将非常可靠,这不会是一个问题。只要严格遵守事务边界(在处理完成之前不要提交出列操作),就永远不会丢失消息/更新
提高可伸缩性的技巧(每秒执行的次数高)
读和读。要实现高吞吐量处理,您必须将队列中的数据排出来并分批处理(TOP(1000)
)到@table
变量中。有关可应用于处理一批消息的模式,请参阅。你需要阅读和理解
如果出现死锁,我可以设置重试吗
不需要,SSB激活将为您重试。回滚时,退出队列(RECEIVE
)将回滚,从而使消息再次可供激活,并且该过程将自动重试。请注意,一行5次回滚将触发
最大队列读取器=1,--或10
如果1无法处理负载,请添加更多。只要您理解正确的会话组锁定,并行激活的过程就应该处理无关的业务项,并且永远不会死锁。如果在同一队列上的激活过程实例之间遇到死锁,则意味着会话组逻辑中存在缺陷,并且允许SSB认为不相关(不同组)的消息修改相同的数据库记录(相同的业务实体)并导致死锁
顺便说一句,您还必须在启动器服务队列上有一个已激活的过程。看
是否有一种方法可以对两个存储过程使用一个目标队列来防止死锁
你可以,你应该。没有理由有两个目标服务/队列/过程。向同一服务发送两种不同的消息类型,用于所需的两种操作。然后,激活的过程应根据消息类型执行添加逻辑或更新逻辑
我能做些什么使它更可靠吗?像一个执行错误不应该停止对队列的传入请求
SSB激活将非常可靠,这不会是一个问题。只要严格遵守事务边界(在处理完成之前不要提交出列操作),就永远不会丢失消息/更新
提高可伸缩性的技巧(每秒执行的次数高)
读和读。要实现高吞吐量处理,您必须将队列中的数据排出来并分批处理(TOP(1000)
)到@table
变量中。请参阅以了解模式