1. 程式人生 > >SQL Server 事件通知(Event notifications)

SQL Server 事件通知(Event notifications)

一.本文所涉及的內容(Contents)

二.背景(Contexts)

  SQL Server事件通知有什麼用呢?如果你想監控SQL Server的DDL操作,你可以通過DDL觸發器(參考:SQL Server DDL觸發器運用),也可以通過SQL Server 事件通知把這個事件相關的資訊傳送到 Service Broker 服務;他們最大的區別就是DDL觸發器可以進行ROLLBACK,而事件通知不行;還有,事件通知是非同步傳送訊息的;

  SQL Server 事件通知還可以響應部分SQL跟蹤事件,即SQL Trace (參考:SQL Server 預設跟蹤(Default Trace)

SQL Server 建立跟蹤);他們最大的區別就是跟蹤事件可以自定義生成哪些資料列,而事件通知是生成固定的XML;還有,每次重新啟動伺服器時,都必須重新啟動跟蹤。

三.基礎知識(Rudimentary Knowledge)

  事件通知將有關事件的資訊傳送給 Service Broker 服務。執行事件通知可對各種 Transact-SQL 資料定義語言 (DDL) 語句和 SQL跟蹤事件做出響應,並將這些事件的相關資訊傳送到 Service Broker 服務。

事件通知可以用來執行以下操作:

  • 記錄和檢索發生在資料庫上的更改或活動。
  • 執行操作以非同步(asynchronous)方式而不是同步方式響應事件。

  可以將事件通知用作替代 DDL 觸發器和 SQL 跟蹤的程式設計方法,因為你可以通過讀取Service Broker 服務中的佇列,在程式中對資訊進行處理。

  事件資訊作為 xml 型別的變數傳遞給 Service Broker 服務,它提供了有關事件的發生時間、受影響的資料庫物件、涉及的 Transact-SQL 批處理語句的資訊以及其他資訊。

 下圖是我對事件通知邏輯關係的理解,當資料庫A或者例項B產生DDL就會激發事件通知,這個通知把相應的DDL的XML資訊傳送給佇列,你可以通過SQL獲取到佇列中的XML;

wps_clip_image-22400

(Figure1:事件通知邏輯關係圖)

  建立事件通知的event_type引數 可以為 Transact-SQL DDL 事件型別、SQL 跟蹤事件型別或 Service Broker 事件型別有關限定 Transact-SQL DDL 事件型別的列表,請參閱

DDL事件。 Service Broker 事件型別為 QUEUE_ACTIVATION 和 BROKER_QUEUE_DISABLED。 有關詳細資訊,請參閱事件通知

四.事件通知監控DDL(NotifyQueue_DDL)

  資料庫的DDL操作預設會被記錄到Default Trace預設跟蹤中(參考:SQL Server 預設跟蹤(Default Trace)),這是一個被動式的監控;而主動式的監控就可以使用DDL觸發器(參考:SQL Server DDL觸發器運用);我們還可以使用事件通知的形式監控DDL,下面就著重講講實現過程。

下面建立一個SSB_DB資料庫,捕獲資料庫例項的DDL語句;

--Step1:建立示例資料庫
USE master
GO
IF EXISTS(SELECT name FROM sys.databases WHERE name = 'SSB_DB')
DROP DATABASE SSB_DB
GO
CREATE DATABASE SSB_DB
GO

USE SSB_DB
GO
--Step2:建立佇列,預設為開啟
CREATE QUEUE NotifyQueue_DDL
--WITH STATUS=ON
GO

--Step3:建立服務
CREATE SERVICE NotifyService_DDL
ON QUEUE NotifyQueue_DDL
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
GO

--Step4:對系統目錄檢視sys.databases進行查詢
SELECT service_broker_guid
FROM sys.databases
WHERE name = 'SSB_DB'
/*
DB19CBE8-0581-4604-B44A-812C29A565BB
*/

--Step5:建立事件通知,使用上面返回的GUID值
CREATE EVENT NOTIFICATION NotifyEvent_DDL
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
TO SERVICE 'NotifyService_DDL',
'DB19CBE8-0581-4604-B44A-812C29A565BB';

--Step6:測試
CREATE TABLE TestTable (a int)
GO
DROP TABLE TestTable;
GO

--Step7:使用Select或Recieve(其中Recieve會刪除佇列中的事件訊息)查詢佇列
SELECT CAST(message_body as xml) EventInfo
FROM dbo.NotifyQueue_DDL

上面的SQL指令碼,你需要注意以下幾點:

1. 建立的EVENT NOTIFICATION是針對當前資料庫的(ON DATABASE),只有在當前資料庫發生的DDL才會被捕獲;

2. 使用DDL_DATABASE_LEVEL_EVENTS將會監控當前資料庫所有DDL事件,你可以使用DDL_SERVER_LEVEL_EVENTS監控資料庫例項的DDL操作,更多的DDL事件可以參考:DDL 事件組

3. 關於service_broker_guid,你應該通過查詢確認這個值,它的作用是指定解析 broker_service 所依據的 Service Broker 例項。

執行Step7返回下圖的結果,這是執行Step6指令碼產生的兩條DDL訊息:

wps_clip_image-31372

(Figure3:DDL事件在佇列中的XML資訊)

通過下面的SQL指令碼可以對Figure4所示的XML進行解釋,儲存到表中:

執行Step10將返回Figu

--Step8:建立表
CREATE TABLE [dbo].[EventInfo](
    [EventInfoID] [int] IDENTITY(1,1) NOT NULL,
    [PostTime] [datetime] NOT NULL,
    [ServerName] [sysname] NOT NULL,
    [LoginName] [sysname] NOT NULL,
    [DatabaseUser] [sysname] NOT NULL,
    [DatabaseName] [sysname] NOT NULL,
    [Schema] [sysname] NULL,
    [Object] [sysname] NULL,
    [TSQL] [nvarchar](max) NOT NULL,
    [Event] [sysname] NOT NULL,
    [XmlEvent] [xml] NOT NULL,
 CONSTRAINT [PK_EventInfo_EventInfoID] PRIMARY KEY NONCLUSTERED 
(
    [EventInfoID] ASC
) ON [PRIMARY]
) ON [PRIMARY]

--Step9:建立分離XML的儲存過程
-- =============================================
-- Author:        <聽風吹雨>
-- Create date:    <2013.06.19>
-- Description:    <分離XML>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE PROCEDURE sp_SeparateXML

AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @data XML
    DECLARE @itemCur CURSOR
    SET @itemCur = CURSOR FOR 
        SELECT CAST(message_body as xml) EventInfo
            FROM [SSB_DB].[dbo].NotifyQueue_DDL

    OPEN @itemCur
    FETCH NEXT FROM @itemCur INTO @data
    WHILE @@FETCH_STATUS=0
    BEGIN
    --邏輯處理
    INSERT [SSB_DB].[dbo].[EventInfo](
        [PostTime],
        [ServerName],
        [LoginName],
        [DatabaseUser],
        [DatabaseName],
        [Schema],
        [Object],
        [TSQL],
        [Event],
        [XmlEvent])
    VALUES(
        @data.value('(/EVENT_INSTANCE/PostTime)[1]', 'sysname'),
        @data.value('(/EVENT_INSTANCE/ServerName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/UserName)[1]', 'sysname'),
        @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname'),  
        @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname'), 
        @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'),
        @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname'),
        @data
        );
    FETCH NEXT FROM @itemCur INTO @data
    END 

    CLOSE @itemCur
    DEALLOCATE @itemCur

END
GO

--Step10:解析XML
TRUNCATE TABLE [EventInfo]
EXEC dbo.sp_SeparateXML
SELECT * FROM [EventInfo]

re4和Figure5的結果,這就是分解後的效果。

wps_clip_image-9459

(Figure4:結構化XML返回列表)

wps_clip_image-12939

(Figure5:結構化XML返回列表補充)

上面Step9是使用遊標的形式獲取XML事件資訊,下面Step11以RECEIVE方式獲取XML事件資訊,這種形式會把訊息從佇列中刪除。

--Step11:建立分離XML的儲存過程
-- =============================================
-- Author:        <聽風吹雨>
-- Create date:    <2013.06.20>
-- Description:    <RECEIVE方式分離XML>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE PROCEDURE [dbo].[sp_SeparateXML_RECEIVE]

AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @data XML;
    DECLARE @RecvReplyDlgHandle UNIQUEIDENTIFIER;
    BEGIN TRANSACTION;
    WAITFOR
    ( RECEIVE TOP(1)
        @RecvReplyDlgHandle = conversation_handle,
        @data = CAST(message_body as xml)
      FROM dbo.NotifyQueue_DDL
    ), TIMEOUT 1000;

    --END CONVERSATION @RecvReplyDlgHandle;
    
    IF (@data IS NOT NULL)
    BEGIN
        --邏輯處理
        INSERT [SSB_DB].[dbo].[EventInfo](
            [PostTime],
            [ServerName],
            [LoginName],
            [DatabaseUser],
            [DatabaseName],
            [Schema],
            [Object],
            [TSQL],
            [Event],
            [XmlEvent])
        VALUES(
            @data.value('(/EVENT_INSTANCE/PostTime)[1]', 'sysname'),
            @data.value('(/EVENT_INSTANCE/ServerName)[1]', 'sysname'), 
            @data.value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname'), 
            @data.value('(/EVENT_INSTANCE/UserName)[1]', 'sysname'),
            @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname'),  
            @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname'), 
            @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname'), 
            @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'),
            @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname'),
            @data
            );
    END
    COMMIT TRANSACTION;

END
GO


--Step12:解析XML
TRUNCATE TABLE [EventInfo]
DECLARE @counts INT
SELECT @counts = COUNT(1) FROM dbo.NotifyQueue_DDL
WHILE(@counts > 0)
BEGIN
    EXEC dbo.sp_SeparateXML_RECEIVE
    SET @counts = @counts - 1
END
SELECT * FROM [EventInfo]

五.事件通知監控SQL跟蹤事件(NotifyQueue_Trace)

  關於使用事件通知監控SQL跟蹤事件的文件我基本沒有看到過,而且CSDN也沒有相關的T-SQL示例,下面就演示實現過程:

USE SSB_DB
GO
--Step13:建立佇列
CREATE QUEUE NotifyQueue_Trace
GO

--Step14:建立服務
CREATE SERVICE NotifyService_Trace
ON QUEUE NotifyQueue_Trace
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
GO

--Step15:對系統目錄檢視sys.databases進行查詢
SELECT service_broker_guid
FROM sys.databases
WHERE name = 'SSB_DB'
/*
DB19CBE8-0581-4604-B44A-812C29A565BB
*/

--Step16:建立使用者登陸事件通知
CREATE EVENT NOTIFICATION NotifyEvent_Trace
ON SERVER
FOR AUDIT_LOGIN
TO SERVICE 'NotifyService_Trace',
'DB19CBE8-0581-4604-B44A-812C29A565BB';

--Step17:測試登陸,如Figure6所示


--Step18:使用Select或Recieve(其中Recieve會刪除佇列中的事件訊息)查詢佇列
SELECT CAST(message_body as xml) EventInfo
FROM dbo.NotifyQueue_Trace
ORDER BY message_body DESC

--Step19:查詢錯誤日誌
EXEC xp_readerrorlog 0,1,NULL,NULL,NULL,NULL,'DESC'

Step17可以通過Figure6所示的方式進行測試,執行Step18的SQL指令碼返回Figure7資訊,從內容看的確是監控到了使用者登入的資訊,同樣我們可以通過Step19的SQL指令碼檢視錯誤日誌的,效果是一樣的。

wps_clip_image-19720

(Figure6:測試登陸)

wps_clip_image-14005

(Figure7:SQL跟蹤事件AUDIT_LOGIN)

wps_clip_image-1394

(Figure8:ERRORLOG讀取)

  對比Figure7和Figure8的事件時間,可以看到事件先寫入了錯誤日誌中,再非同步寫入到事件通知的訊息佇列中;

  其它可以監控的SQL跟蹤事件可以通過用於事件通知的跟蹤事件進行檢視,只有部分的SQL跟蹤事件可用於事件通知,注意,不是所有。

六.注意事項(Attention)

1. SERVER:將事件通知的作用域應用於 SQL Server 的當前例項。 如果已指定,則只要 FOR 子句中的指定事件在 SQL Server 的例項中發生,便會激發通知。

2. DATABASE:將事件通知的作用域應用於當前資料庫。 如果已指定,則只要 FOR 子句中的指定事件在當前資料庫中發生,便會激發通知。

3. 如果你建立了是SERVER級別的事件通知,那麼在每個資料庫的[server_events] 和[server_event_notifications]檢視中都能看到相同的事件通知資訊;

wps_clip_image-4192

(Figure9:伺服器級別事件通知)

4. 在獲取佇列資料的時候有兩種方式,Select或Recieve(其中Recieve會刪除佇列中的事件訊息);

5. 在ON DATABASE的時候,不能使用FOR DDL_SERVER_LEVEL_EVENTS,不然會報下面的錯誤資訊:

訊息1098,級別15,狀態1,第2 行

指定的事件型別對指定的目標物件無效。

6. 無論是否回滾 DDL 語句,都將始終生成 DDL 生成的跟蹤事件。如果回滾相應 DDL 語句中的事件,則事件通知不會觸發。

7. 建立事件通知的時候指定解析 broker_service 所依據的 Service Broker 例項。 特定 Service Broker 的值可通過查詢 sys.databases 目錄檢視的 service_broker_guid 列來獲取。 使用 'current database' 在當前資料庫中指定 Service Broker 例項。 'current database' 是不區分大小寫的文字字串。

9. 建立MESSAGE TYPE和CONTRACT的時候,名稱的規範建議使用類似URL格式:[//AdventureWorks.com/Helpdesk/SupportTicket]

“The message_type_namevalue must be unique within the database and commonly uses a URL (or URL-like) convention that allows you to create a hierarchical namespace for cre-ating multiple message types for different services.”

七.疑問(Questions)

1. “SQL 跟蹤不會對與事務關聯的效能造成負面影響。打包資料很有效。建立 XML 格式的事件資料和傳送事件通知會對效能造成關聯的負面影響。“這句需要怎麼理解?

2. 使用CREATE EVENT NOTIFICATION建立的事件通知在SSMS哪裡能找到呢?

解答:只能在動態檢視sys.event_notifications和sys.server_event_notifications看到相關的建立資訊;(依據:只能使用 Transact-SQL 語句建立事件通知)

3. 觸發器必須在本地伺服器上處理,事件通知可以在遠端伺服器上處理?如何實現?什麼場景下可以使用?

4. 事件通知的SQL跟蹤只能設定SERVER級別?不能設定DATABASE級別的?如果設定了DATABASE級別會報下面的錯誤資訊:

訊息1098,級別15,狀態1,第2 行

指定的事件型別對指定的目標物件無效。

解答:SQL 跟蹤事件只能運行於伺服器級 (ON SERVER)(依據:用於事件通知的跟蹤事件

5. 為什麼一次登陸會造成兩條登陸資訊的呢?下面的SQL語句會造成一次使用者登陸,因為需要讀取檔案?

EXEC xp_readerrorlog 0,1,NULL,NULL,NULL,NULL,'DESC'

wps_clip_image-31448

(Figure10:登陸資訊)

6. 佇列是儲存在什麼地方?以什麼的形式儲存的?儲存怎麼保證訊息的安全性(資料庫宕機或者資料丟失等情況)?

解答:從下圖看來,佇列是儲存在主檔案組中的。

wps_clip_image-1943

(Figure11:佇列屬性)

八.參考文獻(References)

事件通知

DDL 事件