Cara membuat Pemberitahuan Acara yang menjalankan Pekerjaan / prosedur saat mencerminkan perubahan keadaan

11

Saya mengajukan pertanyaan ini dalam urutan yang satu ini. Dapatkah saya mengirim string melalui TCP menggunakan T-SQL?

Remus Rusanu memaparkan apa yang tampaknya menjadi solusi optimal untuk masalah saya, tapi ... Saya terlalu tidak dewasa untuk mengerti dan membuat semua yang dia katakan.

Sejauh ini saya pikir apa yang saya butuhkan untuk membuat acara pemberitahuan untuk DATABASE_MIRRORING_STATE_CHANGE, apakah saya benar?

bagaimana cara membuat pemberitahuan acara ini ketika dipicu memasukkan garis dalam tabel, yang menyimpan stempel waktu dan ID yang berasal dari pemberitahuan.

sejauh ini saya sedang menyiapkan satu peringatan per ID, masing-masing menjalankan pekerjaan seperti ini (contoh ini untuk 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)

Pada dasarnya saya membuat log internal di database ini:

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]

Tapi dengan cara ini ... peringatan tidak dipicu cukup cepat ... jadi saya kehilangan informasi ...

Bisakah Anda memberi tahu saya cara memprogram perilaku ini dengan membuat notifikasi acara untuk acara Mirroring State Changeed Database ?

Salam Hormat

RagnaRock
sumber

Jawaban:

13

Langkah 1: Buat layanan untuk menerima notifikasi dan antrian untuk itu:

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

Perhatikan bahwa saya menggunakan msdb, ini bukan kecelakaan. Karena pemberitahuan peristiwa tingkat server dikirim dari msdbsana, jauh lebih baik jika Anda membuat titik akhir percakapan yang berlawanan (target) juga msdb, yang menyiratkan bahwa layanan tujuan dan antrian juga harus digunakan msdb.

Langkah 2: buat prosedur pemrosesan pemberitahuan acara:

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

Prosedur broker layanan penulisan bukanlah kode run-of-the-mill Anda. Seseorang harus mengikuti standar tertentu dan sangat mudah menyimpang ke wilayah pasir isap. Kode ini menunjukkan beberapa praktik yang baik:

  • bungkus dequeue pesan dan pemrosesan dalam transaksi. Tidak punya otak, jelas.
  • selalu periksa jenis pesan yang diterima. Prosedur broker layanan yang baik harus menangani Errordan EndDialogpesan dengan tepat dengan mengakhiri dialog dari sisi itu. Tidak melakukan hal itu mengakibatkan kebocoran pegangan ( sys.conversation_endpointstumbuh)
  • selalu periksa apakah ada pesan yang keluar dari RECEIVE. Beberapa sampel memeriksa @@ rowcount setelah RECEIVE, yang sangat OK. Kode sampel ini bergantung pada pemeriksaan nama pesan (tidak ada pesan yang menyiratkan nama jenis pesan NULL) dan menangani kasus tersebut secara implisit.
  • buat tabel kesalahan pemrosesan. sifat latar belakang prosedur yang diaktifkan SSB membuatnya sangat sulit untuk memecahkan kesalahan jika pesan hilang begitu saja tanpa jejak.

Selain itu, kode ini juga melakukan beberapa kode praktik baik terkait tugas yang ada (memantau DBM):

  • bedakan antara post_time( kapan pemberitahuan dikirim? ), start_time( kapan tindakan yang memicu pemberitahuan dimulai? ) dan processing_time( kapan pemberitahuan diproses? ). post_timedan start_timekemungkinan akan identik atau sangat dekat, tetapi processing_timedapat menjadi detik, jam, hari terpisah dari post_time. yang menarik untuk audit biasanya post_time.
  • karena post_timedan processing_timeberbeda, harus jelas bahwa tugas pemantauan DBM dalam prosedur yang diaktifkan notifikasi bahkan tidak memiliki bisnis sys.database_mirroringmelihat . Pandangan itu akan menunjukkan keadaan saat ini pada saat pemrosesan, yang mungkin atau mungkin tidak terkait dengan acara tersebut. Jika pemrosesan terjadi lama setelah acara diposting (pikirkan waktu henti pemeliharaan) daripada masalahnya jelas, tetapi dapat menangani juga dalam pemrosesan 'sehat' jika perubahan DBM menyatakan sangat cepat dan memposting dua (atau lebih) peristiwa dalam baris (yang sering terjadi): dalam situasi ini pemrosesan, seperti dalam kode yang Anda poskan, mengaudit peristiwa saat itu terjadi, tetapi akan mencatat keadaan saat ini, final , saat ini. Membaca audit semacam itu bisa sangat membingungkan nantinya.
  • selalu mengaudit acara XML asli. Dengan cara ini nanti Anda dapat meminta XML ini untuk informasi apa pun yang tidak 'robek' ke dalam kolom di tabel audit.

Langkah 3: lampirkan prosedur ke antrian:

alter queue dbm_notifications_queue
with activation (
    status=on,
    procedure_name = [dbm_notifications_procedure],
    max_queue_readers = 1,
    execute as  owner);
Remus Rusanu
sumber
Jadi saya harus melakukan ini di kedua Mitra, bukan? Jika terjadi kegagalan pada Kepala Sekolah tanpa Saksi, apakah ada cara untuk memproses / memeriksa antrian? untuk mengetahui apakah saya memiliki akses ke semua situasi perubahan negara, atau jika ada sesuatu yang belum masuk dalam tabel notifikasi saya
RagnaRock
Anda harus melakukannya pada kedua pasangan, benar. Jika terjadi kegagalan pada Principal jika msdbmasih online (mis. Kegagalan adalah kegagalan DB, bukan kegagalan server) maka pemrosesan antrian akan terjadi.
Remus Rusanu
Terima kasih atas penghargaannya. Paling tidak, Anda sekarang memiliki salinan "Pro SQL Server 2008 Mirroring" yang saya dengar adalah buku yang bagus tentang topik ini.
Remus Rusanu
9

Saya harus membeli "Pro SQL Server 2008 Mirroring" setelah membaca bab 6, saya menemukan langkah-langkah untuk melakukan ini adalah:

periksa apakah broker layanan diaktifkan

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

jika tidak, jalankan

ALTER DATABASE DataBaseName set ENABLE_BROKER;

buat prosedur tersimpan yang kami inginkan untuk dipicu ketika acara pemberitahuan tiba:

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

buat antrian, untuk menjadi semacam perantara antara layanan dan prosedur tersimpan yang ingin kita picu

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

buat layanan yang akan dikaitkan dengan acara tersebut

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

Buat rute

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

dan kemudian buat Pemberitahuan Acara

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