1 단계 : 알림 및이를 수신 할 서비스를 작성하십시오.
use msdb;
go
create queue dbm_notifications_queue;
create service dbm_notification_service
on queue dbm_notifications_queue
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
go
create event notification dbm_notifications
on server
for database_mirroring_state_change
to service N'dbm_notification_service', N'current database';
go
내가 사용 msdb
하고 있습니다. 이것은 사고가 아닙니다. 서버 수준 이벤트 알림은에서 전송되기 때문에 msdb
반대 대화 끝점 (대상)도 만들면 훨씬 좋습니다. msdb
즉, 대상 서비스와 큐도에 배포해야합니다 msdb
.
2 단계 : 이벤트 알림 처리 절차를 만듭니다.
use msdb;
go
create table dbm_notifications_errors (
incident_time datetime not null,
session_id int not null,
has_rolled_back bit not null,
[error_number] int not null,
[error_message] nvarchar(4000) not null,
[message_body] varbinary(max));
create clustered index cdx_dbm_notifications_errors
on dbm_notifications_errors (incident_time);
go
create table mirroring_alerts (
alert_time datetime not null,
start_time datetime not null,
processing_time datetime not null,
database_id smallint not null,
database_name sysname not null,
[state] tinyint not null,
[text_data] nvarchar(max),
event_data xml not null);
create clustered index cdx_mirroring_alerts
on mirroring_alerts (alert_time);
go
create procedure dbm_notifications_procedure
as
begin
declare @dh uniqueidentifier, @mt sysname, @raw_body varbinary(max), @xml_body xml;
begin transaction;
begin try;
receive top(1)
@dh = conversation_handle,
@mt = message_type_name,
@raw_body = message_body
from dbm_notifications_queue;
if N'http://schemas.microsoft.com/SQL/Notifications/EventNotification' = @mt
begin
set @xml_body = cast(@raw_body as xml);
-- shred the XML and process it accordingly
-- IMPORTANT! IMPORTANT!
-- DO NOT LOOK AT sys.database_mirroring
-- The view represents the **CURRENT** state
-- This message reffers to an **EVENT** that had occured
-- the current state may or may no be relevant for this **PAST** event
declare @alert_time datetime
, @start_time datetime
, @processing_time datetime = getutcdate()
, @database_id smallint
, @database_name sysname
, @state tinyint
, @text_data nvarchar(max);
set @alert_time = @xml_body.value (N'(//EVENT_INSTANCE/PostTime)[1]', 'DATETIME');
set @start_time = @xml_body.value (N'(//EVENT_INSTANCE/StartTime)[1]', 'DATETIME');
set @database_id = @xml_body.value (N'(//EVENT_INSTANCE/DatabaseID)[1]', 'SMALLINT');
set @database_name = @xml_body.value (N'(//EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME');
set @state = @xml_body.value (N'(//EVENT_INSTANCE/State)[1]', 'TINYINT');
set @text_data = @xml_body.value (N'(//EVENT_INSTANCE/TextData)[1]', 'NVARCHAR(MAX)');
insert into mirroring_alerts (
alert_time,
start_time,
processing_time,
database_id,
database_name,
[state],
text_data,
event_data)
values (
@alert_time,
@start_time,
@processing_time,
@database_id,
@database_name,
@state,
@text_data,
@xml_body);
end
else if N'http://schemas.microsoft.com/SQL/ServiceBroker/Error' = @mt
begin
set @xml_body = cast(@raw_body as xml);
DECLARE @error INT
, @description NVARCHAR(4000);
WITH XMLNAMESPACES ('http://schemas.microsoft.com/SQL/ServiceBroker/Error' AS ssb)
SELECT @error = CAST(@xml_body AS XML).value('(//ssb:Error/ssb:Code)[1]', 'INT'),
@description = CAST(@xml_body AS XML).value('(//ssb:Error/ssb:Description)[1]', 'NVARCHAR(4000)');
insert into dbm_notifications_errors(
incident_time,
session_id,
has_rolled_back,
[error_number],
[error_message],
[message_body])
values (
getutcdate(),
@@spid,
0,
@error,
@description,
@raw_body);
end conversation @dh;
end
else if N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' = @mt
begin
end conversation @dh;
end
commit;
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 dbm_notifications_errors(
incident_time,
session_id,
has_rolled_back,
[error_number],
[error_message],
[message_body])
values (
getutcdate(),
@@spid,
@has_rolled_back,
@error_number,
@error_message,
@raw_body);
if (@has_rolled_back = 0)
begin
commit;
end
end catch
end
go
Service Broker 프로 시저 작성은 표준 코드가 아닙니다. 하나는 특정 표준을 따라야하며 퀵 샌드 영역으로 빠져 나가기가 매우 쉽습니다. 이 코드는 몇 가지 모범 사례를 보여줍니다.
- 메시지 큐와 처리를 트랜잭션으로 랩핑하십시오. 더 똑똑하지 않습니다.
- 수신 된 메시지 유형을 항상 확인하십시오. 올바른 서비스 브로커 프로시 저는 대화 상자를 종료하여 메시지 와 메시지를 적절하게 처리 해야합니다 . 그렇지 않으면 핸들 누수가 발생합니다 ( 증가).
Error
EndDialog
sys.conversation_endpoints
- 항상 RECEIVE에 의해 메시지가 대기열에서 제거되었는지 확인하십시오. 일부 샘플은 check @@ rowcount 이후
RECEIVE
에 완벽하게 확인됩니다. 이 샘플 코드는 메시지 이름 검사 (메시지가 NULL 메시지 유형 이름을 의미하지 않음)를 사용하여 해당 경우를 암시 적으로 처리합니다.
- 처리 오류 테이블을 작성하십시오. SSB 활성화 절차의 배경 특성으로 인해 메시지가 추적없이 사라지면 오류를 해결하기가 실제로 어렵습니다.
또한이 코드는 현재 수행중인 작업 (DBM 모니터링)과 관련하여 유용한 코드도 제공합니다.
post_time
( 알림이 언제 전송 되었습니까? ), start_time
( 알림을 트리거 한 작업이 언제 시작 되었습니까? ) 및 processing_time
( 알림이 언제 처리 되었습니까? )를 구별 하십시오 . post_time
그리고 start_time
가능성이 매우 가까이 동일하거나 수 있지만 processing_time
떨어져에서 초, 시간, 일 수 있습니다 post_time
. 감사의 흥미로운 점은 대개 post_time
입니다.
- 이후
post_time
와는 processing_time
다른, 더 통지에 작업을 모니터링 DBM이 절차가 활성화 분명해야한다 보고 어떤 비즈니스가 없습니다 sys.database_mirroring
보기 . 이보기 에는 처리 시점의 현재 상태 가 표시 되며 이벤트와 관련이있을 수도 있고 그렇지 않을 수도 있습니다. 이벤트가 게시 된 후 (유지 보수 다운 타임을 생각한 후) 처리가 오랜 시간 동안 발생하는 경우 문제는 분명하지만 DBM이 상태를 매우 빠르게 변경하고 두 개 이상의 이벤트를 게시하면 '정상적인'처리에서도 처리 할 수 있습니다. 행 (자주 발생) :이 상황에서 게시 한 코드에서와 같이 처리는 이벤트가 발생할 때 이벤트를 감사하지만 현재의 final 상태를 기록합니다 . 이러한 감사를 읽는 것은 나중에 매우 혼란 스러울 수 있습니다.
- 항상 원본 XML 이벤트를 감사하십시오. 이렇게하면 나중에 감사 테이블의 열로 '파쇄'되지 않은 정보에 대해이 XML을 쿼리 할 수 있습니다.
3 단계 : 프로 시저를 큐에 첨부하십시오.
alter queue dbm_notifications_queue
with activation (
status=on,
procedure_name = [dbm_notifications_procedure],
max_queue_readers = 1,
execute as owner);