Cách tạo Thông báo sự kiện chạy Công việc / quy trình khi phản ánh trạng thái thay đổi


11

Tôi đang hỏi câu hỏi này theo trình tự câu hỏi này Tôi có thể gửi một chuỗi qua TCP bằng T-SQL không?

Remus Rusanu tiết lộ những gì có vẻ là một giải pháp tối ưu cho vấn đề của tôi, nhưng ... tôi còn quá non nớt để hiểu và làm mọi thứ anh ta nói.

Cho đến nay tôi nghĩ những gì tôi cần để tạo một sự kiện thông báo cho DATABASE_MIRRORING_STATE_CHANGE, tôi có đúng không?

Làm cách nào tôi có thể tạo thông báo sự kiện này khi kích hoạt nó chèn một dòng trong bảng, lưu trữ dấu thời gian và ID xuất phát từ thông báo.

Cho đến nay tôi đang thiết lập một cảnh báo cho mỗi ID, mỗi ID đang chạy một công việc như thế này (ví dụ này là cho 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)

Về cơ bản tôi đang tạo một nhật ký nội bộ trong cơ sở dữ liệu này:

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]

Nhưng theo cách này ... các cảnh báo không được kích hoạt đủ nhanh ... vì vậy tôi mất thông tin ...

Bạn có thể cho tôi biết cách lập trình hành vi này với việc tạo thông báo sự kiện cho sự kiện Thay đổi trạng thái phản ánh cơ sở dữ liệu không?

Trân trọng

Câu trả lời:


13

Bước 1: Tạo một dịch vụ để nhận thông báo và xếp hàng cho nó:

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

Lưu ý rằng tôi đang sử dụng msdb, đây không phải là một tai nạn. Bởi vì các thông báo sự kiện ở cấp máy chủ được gửi từ msdbnó sẽ tốt hơn nhiều nếu bạn tạo điểm cuối cuộc trò chuyện ngược lại (mục tiêu) msdb, điều này ngụ ý rằng dịch vụ đích và hàng đợi cũng phải được triển khai msdb.

Bước 2: tạo quy trình xử lý thông báo sự kiện:

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

Viết thủ tục môi giới dịch vụ không phải là mã chạy của bạn. Người ta phải tuân theo các tiêu chuẩn nhất định và rất dễ đi lạc vào lãnh thổ cát lún. Mã này cho thấy một số thực tiễn tốt:

  • bọc dequeue tin nhắn và xử lý trong một giao dịch. Không có trí tuệ, rõ ràng.
  • luôn luôn kiểm tra loại tin nhắn nhận được. Một thủ tục môi giới dịch vụ tốt phải xử lý ErrorEndDialognhắn tin một cách thích hợp bằng cách kết thúc hộp thoại từ phía đó. Không làm như vậy dẫn đến xử lý rò rỉ ( sys.conversation_endpointstăng trưởng)
  • luôn luôn kiểm tra nếu một tin nhắn đã được xử lý bằng cách NHẬN. Một số mẫu kiểm tra @@ rowcount sau RECEIVE, điều này hoàn toàn OK. Mã mẫu này dựa trên kiểm tra tên tin nhắn (không có tin nhắn ngụ ý tên loại tin nhắn NULL) và xử lý trường hợp đó hoàn toàn.
  • tạo một bảng lỗi xử lý. bản chất nền của các thủ tục kích hoạt SSB làm cho việc khắc phục lỗi rất khó khăn nếu các thông báo đơn giản biến mất dấu vết.

Ngoài ra, mã này cũng thực hiện một số mã thực hành tốt liên quan đến nhiệm vụ hiện tại (giám sát DBM):

  • phân biệt giữa post_time( khi nào thông báo được gửi? ), start_time( khi nào thì hành động kích hoạt bắt đầu đáng chú ý? ) và processing_time( khi nào thông báo được xử lý? ). post_timestart_timecó thể sẽ giống hệt hoặc rất gần, nhưng processing_timecó thể cách nhau vài giây, vài giờ, vài ngày post_time. một trong những thú vị cho kiểm toán thường là post_time.
  • post_timeprocessing_timekhác nhau, rõ ràng là một nhiệm vụ giám sát DBM trong một quy trình kích hoạt thông báo thậm chí không có doanh nghiệp nhìn vào sys.database_mirroringxem . Quan điểm đó sẽ hiển thị hiện tại trạng thái tại tại thời điểm xử lý, có thể có hoặc không liên quan đến sự kiện. Nếu quá trình xử lý xảy ra trong một thời gian dài sau khi sự kiện được đăng (nghĩ rằng thời gian ngừng bảo trì) thì vấn đề là rõ ràng, nhưng nó cũng có thể xử lý trong quá trình xử lý 'lành mạnh' nếu DBM thay đổi trạng thái rất nhanh và đăng hai (hoặc nhiều) sự kiện trong một hàng (xảy ra thường xuyên): trong tình huống này, việc xử lý, như trong mã bạn đã đăng, kiểm tra sự kiện khi chúng xảy ra, nhưng sẽ ghi lại trạng thái hiện tại, cuối cùng . Đọc một cuộc kiểm toán như vậy có thể rất khó hiểu sau này.
  • luôn kiểm toán sự kiện XML gốc. Bằng cách này, sau này bạn có thể truy vấn XML này cho bất kỳ thông tin nào không được 'băm nhỏ' thành các cột trong bảng kiểm toán.

Bước 3: đính kèm thủ tục vào hàng đợi:

alter queue dbm_notifications_queue
with activation (
    status=on,
    procedure_name = [dbm_notifications_procedure],
    max_queue_readers = 1,
    execute as  owner);

Vậy tôi nên làm điều này trong cả hai Đối tác phải không? Trong trường hợp thất bại trong Hiệu trưởng không có Nhân chứng, có cách nào để xử lý / kiểm tra hàng đợi không? để biết liệu tôi có quyền truy cập vào tất cả các tình huống thay đổi trạng thái hay không, nếu có điều gì đó chưa được đăng nhập trong bảng thông báo của tôi
RagnaRock

Bạn nên làm điều đó trên cả hai đối tác, phải. Trong trường hợp thất bại trên Hiệu trưởng nếu msdbvẫn trực tuyến (ví dụ: lỗi là lỗi DB, không phải lỗi máy chủ) thì việc xử lý hàng đợi sẽ xảy ra.
Remus Rusanu

Cảm ơn giải thưởng. Ít nhất, bây giờ bạn đã có một bản sao của "Pro SQL Server 2008 Mirroring" mà tôi nghe là một cuốn sách hay về chủ đề này.
Remus Rusanu

9

Tôi đã phải mua "Pro SQL Server 2008 Mirroring" sau khi đọc chương 6, tôi đã tìm ra các bước để làm điều này là:

kiểm tra nếu môi giới dịch vụ được kích hoạt

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

nếu không, hãy chạy

ALTER DATABASE DataBaseName set ENABLE_BROKER;

tạo thủ tục lưu trữ mà chúng tôi muốn được kích hoạt khi sự kiện thông báo đến:

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

tạo một hàng đợi, để trở thành một người trung gian giữa dịch vụ và thủ tục được lưu trữ mà chúng tôi muốn kích hoạt

-- 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

tạo dịch vụ sẽ được liên kết với sự kiện

-- 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

Tạo một tuyến đường

-- 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

và sau đó tạo Thông báo sự kiện

-- 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.