SQL Server 事件通知(Event notifications)

2013-12-13 17:21 by 听风吹雨, 333 阅读, 3 评论, 收藏编辑

一、 背景

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;还有,每次重新启动服务器时,都必须重新启动跟踪。

二、 基础知识

事件通知将有关事件的信息发送给 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;

(Figure1:事件通知逻辑关系图)

创建事件通知的event_type参数 可以为 Transact-SQL DDL 事件类型、SQL 跟踪事件类型或 Service Broker 事件类型有关限定 Transact-SQL DDL 事件类型的列表,请参阅 DDL事件。 Service Broker 事件类型为 QUEUE_ACTIVATION 和 BROKER_QUEUE_DISABLED。 有关详细信息,请参阅事件通知

三、 事件通知监控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消息:

(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的结果,这就是分解后的效果。

(Figure4:结构化XML返回列表)

(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跟踪事件

关于使用事件通知监控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脚本查看错误日志的,效果是一样的。

(Figure6:测试登陆)

(Figure7:SQL跟踪事件AUDIT_LOGIN)

(Figure8:ERRORLOG读取)

对比Figure7和Figure8的事件时间,可以看到事件先写入了错误日志中,再异步写入到事件通知的消息队列中;

其它可以监控的SQL跟踪事件可以通过用于事件通知的跟踪事件进行查看,只有部分的SQL跟踪事件可用于事件通知,注意,不是所有。

五、 注意事项

1. SERVER:将事件通知的作用域应用于 SQL Server 的当前实例。 如果已指定,则只要 FOR 子句中的指定事件在 SQL Server 的实例中发生,便会激发通知。

2. DATABASE:将事件通知的作用域应用于当前数据库。 如果已指定,则只要 FOR 子句中的指定事件在当前数据库中发生,便会激发通知。

3. 如果你创建了是SERVER级别的事件通知,那么在每个数据库的[server_events] 和[server_event_notifications]视图中都能看到相同的事件通知信息;

(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' 是不区分大小写的文字字符串。

8. 只能使用 Transact-SQL 语句创建事件通知。出处:CREATE EVENT NOTIFICATION

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

六、 疑问

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'

(Figure10:登陆信息)

6. 队列是存储在什么地方?以什么的形式存储的?存储怎么保证消息的安全性(数据库宕机或者数据丢失等情况)?

解答:从下图看来,队列是存储在主文件组中的。

(Figure11:队列属性)

七、 参考文献

事件通知

SQL Server 2008中新增的Service Broker事件通知

SQL Server 2008中Service Broker基础应用(上)

SQL Server 2008中Service Broker基础应用(下)

SQL Server 2008中远程Service Broker实现

CREATE EVENT NOTIFICATION (Transact-SQL)中文

sys.event_notification_event_types (Transact-SQL)

DDL 事件

用于事件通知的跟踪事件

DDL 事件组

Event notifications的更多相关文章

  1. SQL Server 事件通知(Event notifications)

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 基础知识(Rudimentary Knowledge) 事件通知监控DDL(NotifyQue ...

  2. java event

    What is an Event? Change in the state of an object is known as event i.e. event describes the change ...

  3. c# 关键字delegate、event(委托与事件)[MSDN原文摘录][1]

    A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. ...

  4. [.NET] - EventLog.EntryWritten Event

    刚看到在MSND论坛上有人问一个EventLog.EntryWritten Event相关的问题,说是在2015触发了一个2013年的EventWritten的事件,比较好奇,然后查看了下这个类: h ...

  5. SQL Server 监控系列(文章索引)

    一.前言(Introduction) SQL Server监控在很多时候可以帮助我们了解数据库做了些什么,比如谁谁在什么时候修改了表结构,谁谁在删除了某个对象,当这些事情发生了,老板在后面追着说这是谁 ...

  6. supervisor的安装与简单介绍

    1,介绍 Supervisor是一个进程管理工具,官方的说法 用途就是有一个进程需要每时每刻不断的跑,但是这个进程又有可能由于各种原因有可能中断.当进程中断的时候我希望能自动重新启动它,此时,我就需要 ...

  7. python supervisor使用

    Supervisor 是基于 Python 的进程管理工具,只能运行在 Unix-Like 的系统上,也就是无法运行在 Windows 上.Supervisor 官方版目前只能运行在 Python 2 ...

  8. supervisor-1:基础篇

    别人博客转载,做个记录 原文链接:http://lixcto.blog.51cto.com/4834175/1539136 有阵子没写博客了,这段时间一直在研究python django框架和前端相关 ...

  9. SQL Server Audit监控触发器状态

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 实现代码(SQL Codes) 注意事项(Attention) 疑问(Questions) 参 ...

随机推荐

  1. unity3d 学习笔记_____Native2d 刚体、冲击、联合使用

    Mass Mass of the rigidbody. Linear Drag Drag coefficient affecting positional movement. Angular Drag ...

  2. SQL Server 有关EXCEPT和INTERSECT使用

    熟练使用SQL Server各种使用会带来多大的方便查询.今天介绍EXCEPT和INTERSECT.请注意,这只是语法SQL Server 2005和以上版本支持. EXCEPT它指的是存在于所述第一 ...

  3. 带你轻松玩转Git--瞬间创建本地仓库

    在上一篇文章中我们对版本控制有了一个比较宏观的了解,同时也能够看到Git 所处在的历史地位.并且对版本控制系统的体系进行了一个宏观的对比,貌似让读者看起来挺复杂的样子. 笔者将会尽可能的简单向大家分享 ...

  4. 阅读《大数据》Tuzipeizhe

    一本好书.4/5明星. 内容:引进美国和信息,相关历史资料.从建国,为了连任奥巴马. 它是引入大型数据在美国,如何从头开始. 的流逝,到近期几年.这股影响美国的大数据 是怎样走入世界,影响各国的. 英 ...

  5. mysql 的load data infile要使用

    LOAD DATA INFILE从文本文件中读出的声明以极高的速度到表. 1.基本语法 LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'fi ...

  6. UVA 11426 - GCD - Extreme (II) (数论)

    UVA 11426 - GCD - Extreme (II) 题目链接 题意:给定N.求∑i<=ni=1∑j<nj=1gcd(i,j)的值. 思路:lrj白书上的例题,设f(n) = gc ...

  7. 【高德地图API】如何获得行政区域?如何制作行政规划图?

    原文:[高德地图API]如何获得行政区域?如何制作行政规划图? 什么是行政规划图?如何获得每个行政区域的边界轮廓图?举例:重庆市 江北区.如图: 官方类参考:http://developer.amap ...

  8. UI 收集

    semantic http://www.semantic-ui.com.cn/modules/reveal.html sbadmin http://startbootstrap.com/templat ...

  9. SEO 优化,网站推广优化教程100条(SEO,网站关键字优化,怎么优化网站,如何优化网站关键字)

    这篇文章不错.  http://www.cnblogs.com/zangdalei/archive/2010/08/31/1814047.html 看了一半之后的,觉得不太靠谱,很多都不懂. 于是 找 ...

  10. AFNetworking3.0 POST请求

    // 请求管理者 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer ...