事件通知有问题。在将消息发送到的机器/驱动器/数据库上(接收者),当没有人看时,驱动器已装满,因此已被整天备份。
现在我们已经释放了驱动器上的空间,它正在接受消息到队列中,但是它似乎没有在处理它们-没有插入新记录,即使队列现在有2200万条消息并且正在增长(!)。队列已启用:
is_activation_enabled = 1
is_receive_enabled = 1
is_enqueue_enabled = 1
我在中看到了激活的SP activation_procedure
,但是当我查看时SP_WHOISACTIVE
,看不到任何活动的阅读器。
在我再次烧毁驱动器之前-我在做什么错?如何获取它来处理或刷新消息?提前致谢。
更新资料
一个想法-自从有了以来is_enqueue_enabled
,也许它可以存储所有消息,直到可以处理所有消息为止?如果是这样,我可以安全地关闭它吗?
CREATE PROCEDURE [dbo].[Parse_EN_Messages]
AS
--mdb 2012/09/05 version 1.2
-- With apologies and thanks to Remus Rusanu, Jonathon Kehayias, Mladen Prajdic, and Jasper Smith for writing
-- about EN, answering questions, and getting the word out about this awesome feature of SQL Server 2005+.
-- Also thanks to Mikael Eriksson for a faster parse with the XML filter.
-- Their code modified, combined, and used below. Any errors herein are mine, not theirs.
-- Part of the code came from MVP Deep Dives Vol 1 Chapter 28 (Mladen), PASS Presentations by Jasper and Jonathon,
-- and Stackexchange (below) from Remus and Mikael Eriksson
-- http://dba.stackexchange.com/questions/10273/how-to-create-an-event-notification-that-runs-a-job-procedure-when-mirroring-sta
-- http://stackoverflow.com/questions/12308099/t-sql-dynamically-filter-xml-on-multiple-conditions/12358926
--History: 1.00 2012/08/27 first release
-- 1.01 2012/09/05 added server-based exclusions and eventsubclass = 0
-- 1.1 2012/09/17 added exclusion_sets which allow multi-condition filtering and improved performance
-- 1.11 2012/10/05 removing the 1=1 as per suggestion by Rusanu;
-- this could cause it to spin forever, blowing out the error_log..and the drive.
-- 1.12 2012/11/14 adding a RETURN in the @@ROWCOUNT. It fails to exit and then hits a COMMIT, causing records
-- in enaudit_error. That was due to the 1.11 change where I no longer use a 1=1.
-- 1.13 2014/01/16 changing ERRORLOG to write the first 500 chars to the CommandText field, as tested in Canada.
SET NOCOUNT ON
DECLARE @message_type NVARCHAR(256),
@message VARBINARY(MAX),
@conversation_handle UNIQUEIDENTIFIER,
@auditdata XML,
@queuing_order BIGINT,
@conversation_group_id UNIQUEIDENTIFIER
BEGIN
BEGIN TRANSACTION;
BEGIN TRY;
WAITFOR (
RECEIVE TOP(1)
@conversation_handle = [conversation_handle], --aka dialog
@conversation_group_id = [conversation_group_id],
@message_type = message_type_name,
@message = message_body,
@queuing_order = queuing_order
FROM dbo.ENAudit_SBQueue --ORDER BY queuing_order --order by doesn't work there.
), TIMEOUT 5000 --we need the timeout so that it won't hold transactions open indefinitely.
IF (@@ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION
--mdb 1.12 2012/11/14 adding a return as otherwise it tries to commit later and fails, causing records in enaudit_error
RETURN
END
SELECT @auditdata = CAST(@message AS XML)
IF @message_type = N'http://schemas.microsoft.com/SQL/Notifications/EventNotification'
-- Dynamically shred the XML and compare to our exclusion table. You should be able to filter on any field.
-- Exclusion set: unique char(2) name. Has to match every condition. Servername is one of the fields handled.
-- Be careful as the filters could impact performance.
and NOT EXISTS --if all active members of the same exclusion_set match, the event is excluded.
(
SELECT * FROM
(
select COUNT(*) AS match_count, exclusion_set
from enaudit_exclusion_list
where exists (
select *
from (
select X.N.value('local-name(.)', 'varchar(128)') as NodeName,
X.N.value('./text()[1]', 'varchar(max)') as NodeValue
from @auditdata.nodes('//*') as X(N)
) T
where T.NodeName = enaudit_exclusion_list.exclusion_type and
T.NodeValue like enaudit_exclusion_list.excluded_value
AND enaudit_exclusion_list.active = 1
)
GROUP BY exclusion_set
) matches_per_set
INNER JOIN
(SELECT COUNT(*) AS total_count, exclusion_set FROM enaudit_exclusion_list WHERE active = 1 GROUP BY exclusion_set) grouped_set
ON match_count = total_count
AND grouped_set.exclusion_set = matches_per_set.exclusion_set
)
BEGIN
INSERT INTO ENAudit_Events
( ServerName ,
queuing_order ,
PostTime ,
StartTime,
EventType ,
SPID ,
LoginName ,
UserName ,
DatabaseName ,
SchemaName ,
ObjectName ,
ObjectType ,
TargetObjectName ,
TargetObjectType ,
CommandText ,
insert_datetime,
message_body_xml
)
--over 128 elements exist, I've chosen the most useful for what I'm doing.
--To get a full list, GROUP BY in the XSD from http://schemas.microsoft.com/sqlserver/2006/11/eventdata/events.xsd
-- More information in EVENTDATA http://msdn.microsoft.com/en-us/library/ms187909.aspx
SELECT
@auditdata.value('(/EVENT_INSTANCE/ServerName)[1]', 'varchar(128)' ) AS ServerName,
@queuing_order AS Queuing_Order,
@auditdata.value('(/EVENT_INSTANCE/PostTime)[1]', 'datetime') AS PostTime,
@auditdata.value('(/EVENT_INSTANCE/StartTime)[1]', 'datetime') AS StartTime,
@auditdata.value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(128)' ) as EventType,
@auditdata.value('(/EVENT_INSTANCE/SPID)[1]', 'bigint') AS SPID,
@auditdata.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(128)' ) AS LoginName,
@auditdata.value('(/EVENT_INSTANCE/UserName)[1]', 'varchar(128)' ) AS UserName,
@auditdata.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'varchar(128)' ) AS DatabaseName,
@auditdata.value('(/EVENT_INSTANCE/SchemaName)[1]', 'varchar(128)' ) AS SchemaName,
@auditdata.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(128)' ) AS ObjectName,
@auditdata.value('(/EVENT_INSTANCE/ObjectType)[1]', 'varchar(128)' ) AS ObjectType,
@auditdata.value('(/EVENT_INSTANCE/TargetObjectName)[1]', 'varchar(128)' ) AS TargetObjectName,
@auditdata.value('(/EVENT_INSTANCE/TargetObjectType)[1]', 'varchar(128)' ) AS TargetObjectType,
--@auditdata.value('(/EVENT_INSTANCE/PropertyName)[1]', 'varchar(128)' ) AS PropertyName,
--@auditdata.value('(/EVENT_INSTANCE/PropertyValue)[1]', 'varchar(128)' ) AS PropertyValue,
--@auditdata.value('(/EVENT_INSTANCE/Parameters)[1]', 'varchar(128)' ) AS Parameters,
CASE @auditdata.value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(128)' )
WHEN 'ERRORLOG' THEN @auditdata.value('/EVENT_INSTANCE[1]/TextData[1]', 'varchar(500)')
ELSE @auditdata.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'varchar(max)' ) END AS CommandText,
GETDATE(),
@auditdata
--Other possibilities for doing a dynamic XML query?
-- http://www.dotnetgenerics.com/Modules/TricksAndTips/SQLServer/DynamicWhereClause.aspx
-- http://www.beefycode.com/post/Expressing-Filter-Queries-as-XML-in-SQL-Server.aspx
-- http://stackoverflow.com/questions/923136/t-sql-filtering-on-dynamic-name-value-pairs
-- http://stackoverflow.com/questions/1729973/filter-sql-queries-on-the-xml-column-using-xpath-xquery
END
else
IF @message_type = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error' --log error messages
BEGIN
WITH XMLNAMESPACES ('http://schemas.microsoft.com/SQL/ServiceBroker/Error' AS ssb)
INSERT INTO ENAudit_Errors ([conversation_group_id], [conversation_handle],
[queuing_order], error_code, error_description, insert_datetime, message_body_raw)
SELECT @conversation_group_id, @conversation_handle, @queuing_order,
@auditdata.value('(//ssb:Error/ssb:Code)[1]', 'INT') AS error_code,
@auditdata.value('(//ssb:Error/ssb:Description)[1]', 'NVARCHAR(4000)') AS error_description,
GETDATE(),
@message
end conversation @conversation_handle --close the conversation if there was an error
END
ELSE
IF @message_type = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
begin
end conversation @conversation_handle;
end
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
declare @xact_state int = xact_state(),
@error_number int = error_number(),
@error_message nvarchar(4000) = error_message(),
@has_rolled_back bit = 0;
if @xact_state = -1
begin
-- Doomed transaction, it must rollback
rollback;
set @has_rolled_back = 1;
end
else if @xact_state = 0
begin
-- transaction was already rolled back (deadlock?)
set @has_rolled_back = 1;
end
insert INTO ENAudit_Errors(
insert_datetime,
error_code,
error_description,
message_body_raw)
values (
getdate(),
@error_number,
@error_message,
@message);
if (@has_rolled_back = 0)
begin
commit;
end
end catch
END
GO
1
因此,根据问题的描述,您正在使用内部激活。sys.dm_broker_activated_tasks中列出了什么吗?您的激活程序是什么样的?您可以在这里发布吗?该过程可能试图立即处理所有消息(即,在一个事务中),这可能很糟糕。同样,没有什么可以阻止您自己手动进行激活。
—
Ben Thul 2015年
由于有毒消息(失败太多),可能禁用了队列。请查询sys.transmission_queue并告诉我们它正在报告什么错误。如果我的怀疑是正确的,那么您应该尝试使用ALTER QUEUE语句重新启动队列。
—
罗伯特·L·戴维斯
@BenThul将激活的存储过程代码添加到该问题。FWIW,我不使用1 = 1。几年前,我与Remus Rusanu进行了交谈,他谈到了毫无理由使用它的原因,我相应地修改了我的产品。话虽如此,我可能应该考虑将其更改为以速度执行100/1000的批处理。但是在备份之前,它一直像冠军一样工作,所以我讨厌触摸它。
—
mbourgon 2015年
我不同意循环对于您现在所处的情况是不必要的。根据BOL的这篇文章(technet.microsoft.com/en-us/library/…),激活不太可能经常触发。这就是为什么在触发该消息时,您希望每个过程都继续处理消息,直到没有剩余要处理的消息为止。只是我的两分钱。为了从现在的漏洞中解脱出来,您可以
—
Ben Thul
while exists (select 1 from dbo.ENAudit_SBQueue) begin exec [dbo].[Parse_EN_Messages]; end
在SSMS中执行类似的操作来伪造它。