미러링 상태 변경시 작업 / 프로 시저를 실행하는 이벤트 알림을 만드는 방법


11

이 질문을 순서대로 묻습니다. T-SQL을 사용하여 TCP를 통해 문자열을 보낼 수 있습니까?

레무스 루사 누 (Remus Rusanu)는 내 문제에 대한 최적의 해결책 인 것처럼 보이는 것을 공개하지만, 나는 자신이 말하는 모든 것을 이해하고 만들기에는 너무 미숙하다.

지금까지 DATABASE_MIRRORING_STATE_CHANGE에 대한 알림 이벤트를 작성하는 데 필요한 것이 맞습니까?

이 이벤트 알림이 트리거되면 테이블에 행을 삽입하여 알림에서 오는 타임 스탬프와 ID를 저장하는 방법을 알 수 있습니다.

지금까지 ID별로 하나의 경고를 설정하고 있습니다. 각 경고는 다음과 같은 작업을 실행합니다 (이 예는 ID = 1입니다).

    DECLARE @state AS varchar(50);
    SELECT @state = mirroring_state_desc FROM SYS.database_mirroring WHERE mirroring_guid IS NOT NULL;
    IF (@state IS null) SET @state = ' ';
    INSERT INTO MirroringAlerts (DateTime, alertID, alertDesc, Sync, alertCreator) values (SYSDATETIME(), 1, 'Principal synchronized with W ', @state, @@SERVERNAME)

기본적 으로이 데이터베이스에 내부 로그를 작성 중입니다.

CREATE TABLE [dbo].[MirroringAlerts](
    [DateTime] [datetime] NOT NULL,
    [alertID] [smallint] NOT NULL,
    [alertDesc] [nchar](50) NOT NULL,
    [Sync] [nchar](12) NOT NULL,
    [alertCreator] [nchar](128) NULL
) ON [PRIMARY]

그러나 이렇게하면 ... 알림이 충분히 빨리 트리거되지 않습니다 ... 그래서 정보를 잃어 가고 있습니다 ...

Database Mirroring State Changed 이벤트에 대한 이벤트 알림 작성 으로이 동작을 프로그래밍하는 방법을 알려주시겠습니까 ?

친애하는

답변:


13

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 프로 시저 작성은 표준 코드가 아닙니다. 하나는 특정 표준을 따라야하며 퀵 샌드 영역으로 빠져 나가기가 매우 쉽습니다. 이 코드는 몇 가지 모범 사례를 보여줍니다.

  • 메시지 큐와 처리를 트랜잭션으로 랩핑하십시오. 더 똑똑하지 않습니다.
  • 수신 된 메시지 유형을 항상 확인하십시오. 올바른 서비스 브로커 프로시 저는 대화 상자를 종료하여 메시지 와 메시지를 적절하게 처리 해야합니다 . 그렇지 않으면 핸들 누수가 발생합니다 ( 증가).ErrorEndDialogsys.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);

두 파트너 모두에서이 작업을 수행해야합니까? 증인이없는 교장에 장애가 발생한 경우 대기열을 처리 / 확인하는 방법이 있습니까? 모든 변경 상태 상황에 액세스 할 수 있는지 또는 알림 테이블에 기록되지 않은 내용이
있는지 알기 위해

두 파트너 모두에서해야합니다. msdb여전히 온라인 상태 인 경우 교장에서 장애가 발생한 경우 (즉, 장애는 서버 장애가 아닌 DB 장애) 대기열 처리가 수행됩니다.
Remus Rusanu

수상 해 주셔서 감사합니다. 최소한 지금은 "Pro SQL Server 2008 미러링"의 사본을 가지고 있습니다.
Remus Rusanu

9

6 장을 읽은 후에 "Pro SQL Server 2008 미러링"을 구입해야했습니다.이를 수행하는 단계는 다음과 같습니다.

서비스 브로커가 활성화되어 있는지 확인

SELECT CASE is_broker_enabled
WHEN 1 Then 'Enabled'
ELSE 'Disabled'
END
FROM sys.databases
WHERE name = 'DataBaseName'

그렇지 않으면 실행

ALTER DATABASE DataBaseName set ENABLE_BROKER;

알림 이벤트가 도착하면 트리거 할 저장 프로 시저를 만듭니다.

CREATE PROCEDURE dbo.dba_MirroringStateChanged
AS
DECLARE @Message XML,
        @DBName sysname,
        @MirrorStateChange INT,
        @ServerName sysname,
        @PostTime datetime,
        @SPID INT,
        @TextData NVARCHAR(500),
        @DatabaseID INT,
        @TransactionsID INT,
        @StartTime datetime;
SET NOCOUNT ON;
-- Receive first unread message in service broker queue
RECEIVE TOP (1)
@Message = CAST(message_body AS XML)
FROM DBMirrorQueue;

BEGIN TRY
    -- Parse state change and database affected
    -- 7 or 8 = database failing over,
    --11 = synchronizing,
    --1 or 2 = synchronized
    SET @MirrorStateChange =
    @Message.value('(/EVENT_INSTANCE/State)[1]', 'int');
    SET @DBName =
    @Message.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname');
    SET @ServerName =
    @Message.value('(/EVENT_INSTANCE/ServerName)[1]', 'sysname');
    SET @PostTime =
    @Message.value('(/EVENT_INSTANCE/PostTime)[1]', 'datetime');
    SET @SPID = @Message.value('(/EVENT_INSTANCE/SPID)[1]', 'int');
    SET @TextData =
    @Message.value('(/EVENT_INSTANCE/TextData)[1]', 'nvarchar(500)');
    SET @DatabaseID =
    @Message.value('(/EVENT_INSTANCE/DatabaseID)[1]', 'int');
    SET @TransactionsID =
    @Message.value('(/EVENT_INSTANCE/TransactionsID)[1]', 'int');
    SET @StartTime =
    @Message.value('(/EVENT_INSTANCE/StartTime)[1]', 'datetime');
END TRY
    BEGIN CATCH
        PRINT 'Parse of mirroring state change message failed.';
    END CATCH

IF (@MirrorStateChange IN (1,2,3,4,5,6,7,8,9,10,11,12,13))
BEGIN

    DECLARE @state AS varchar(50);
    SELECT @state = mirroring_state_desc FROM SYS.database_mirroring WHERE mirroring_guid IS NOT NULL;
    IF (@state IS null) SET @state = ' ';
    INSERT INTO MirroringAlerts (DateTime, alertID, alertDesc, Sync, alertCreator) values (SYSDATETIME(), @MirrorStateChange , @TextData , @state, @SERVERNAME);

END

서비스와 트리거하려는 저장 프로 시저 사이에 일종의 중개인이되도록 대기열을 만듭니다.

-- Create Queue if not exists
IF NOT EXISTS (SELECT 1
    FROM sys.service_queues
    WHERE name = 'DBMirrorQueue')
BEGIN
    CREATE QUEUE DBMirrorQueue
    WITH ACTIVATION (
    PROCEDURE_NAME = dbo.dba_MirroringStateChanged,
    MAX_QUEUE_READERS = 1,
    EXECUTE AS OWNER);
END

이벤트와 관련된 서비스를 만듭니다

-- Create Service if not exists
IF NOT EXISTS (SELECT 1
    FROM sys.services
    WHERE name = 'DBMirrorService')
BEGIN
    CREATE SERVICE DBMirrorService
    ON QUEUE DBMirrorQueue ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
END

경로 만들기

-- Create Route if not exists
IF NOT EXISTS (SELECT 1
    FROM sys.routes
    WHERE name = 'DBMirrorRoute')
BEGIN
    CREATE ROUTE DBMirrorRoute
    WITH SERVICE_NAME = 'DBMirrorService',
    ADDRESS = 'Local';
END

그런 다음 이벤트 알림을 만듭니다

-- Create Event Notification if not exists
IF NOT EXISTS (SELECT 1
    FROM sys.server_event_notifications
    WHERE name = 'DBMirrorStateChange')
BEGIN
    CREATE EVENT NOTIFICATION DBMirrorStateChange
    ON SERVER
    FOR DATABASE_MIRRORING_STATE_CHANGE
    TO SERVICE 'DBMirrorService', 'current database';
END
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.