剖析SQL Server 2005查询通知之基础篇

 
剖析SQL Server 2005查询通知之基础篇
在本系列文章中,我们将深入探讨如何把.NET 2.0和SQL Server 2005的查询通知特征联合起来,以便通知应用程式何时关键数据发生变化进而达到消除反复查询数据库的目的。
一、引言
数据库应用程式的典型问题之一是更新陈旧的数据。
设想有一个典型的显示产品及其分类的电子商务网站。一个供给商的产品列表很可能并不经常发生变化,而其分类列表甚至更不会频繁更改。然而,在用户每次浏览该网站时,必须从数据库中反复查询这些列表。这显然是一种典型的低效资源利用,研发者和架构师都在绞尽脑汁想办法以减少这种浪费。
缓冲技术正是“最小化”对这种几乎“停滞”的数据进行重复查询的技术之一。这种数据能够被进行一次性查询并存储在一个缓冲区中,而且应用程式能够从缓存中重复地存取数据。偶尔情况下,才更新缓存以得到新数据。但是,围绕更新缓存的时间调度方面出现了几个问题。该多长时间操作一次呢?例如,您每隔多长时间希望您的产品分类改变一次?每隔几个月一次?每隔两个月刷新一次该缓冲区如何?您知道会发生什么吗?就在您刷新缓存之后,分类被更新,而且在下一次刷新前在两个月的时间里他将保持陈旧。
查询通知,是微软的ADO.NET和SQL Server小组协作研发的新成果。简言之,查询通知允许您缓冲数据并且仅在SQL Server中的数据发生变化时才发出通知。一旦接到通知,您就能够刷新您的缓冲区或采取您需要的任何措施。
在SQL Server 2005中引入的一种新特征“Service Broker”使得查询通知成为可能。Service Broker把队列机制引入到数据库管理中,他使用一组队列和服务进行通讯,而服务反过来也知道如何往回通讯以调用相应的实体。其实,这些队列和服务都是一些和表、视图和存储过程相同的类对象。尽管完万能够在SQL Server内使用Service Broker,但是ADO.NET知道如何和Service Broker进行通讯以触发这种机制并且从Service Broker中检索回通知。
注意 当SQL Server中的数据发生改变时,查询通知允许您缓冲数据并且通知您。
在.NET一端,存在很多种“钩入”这种功能的方式。ADO.NET 2.0提供了System.Data.SqlClient.SqlDependency和System.Data.Sql.SqlNotificationRequest类。SqlDependency是SqlNotificationRequest的一种高级实现,并且是当使用ADO.NET 2.0时您最有可能使用的类。ASP.NET 2.0也通过System.Web.Caching.SqlCache-Dependency类(他提供了一个针对SqlDependency的包装器)和Service Broker进行通讯,而且这是直接通过在一个ASP.NET页面中使用<%OutputCache>指令以声明方式提供的功能实现的。这允许ASP.NET研发者容易地实现使依赖于SQL Server中的数据中的缓存无效。
二、.NET和Service Broker的通讯
上面这些技术是如何联合到一起来解决“缓冲之谜”的呢?尽管您能够采取很多的措施以允许SQL Server把服务提供给.NET;但是,关键还在于,发送到SQL Server的查询具备一个依附到他们的标志以便告诉SQL Server,除了返回结果集外,SQL Server还应该把该查询(及其请求者)注册到Service Broker。为此,您要创建一个感知该查询的队列和一个依附到该队列的服务,并且知道如何返回到客户端。假如该结果集中的任何一行在数据库中得到更新,那么在相关队列中的项将触发,并且反过来,把一条消息发送到他的服务,然后把一个通知发送回初始化该请求的应用程式。
图1是SQL Server Management Studio的一个快照,他显示了在数据库的Service Broker部分中的队列(Queues)和服务(Services)。
图1.该图显示了.NET的查询通知所使用的Pubs数据库中的缺省队列和服务。
下面是理解这一过程的一些有关重要内容:
· 存在一些规则以指出SQL Server接收哪些类型的查询。
· 一旦SQL Server发送回通知,队列和服务即被删除。这意味着,您仅能在每次请求中得到一个通知。一个典型的应用程式会重新查询数据库并且,在同时,请求在Service Broker中创建一种新的依赖性。
· 返回到应用程式的信息也但是是“something changed”。该应用程式并不被通知改变了什么(请参考本文中的SQLNotificationEventArgs
节了解更多的信息)。
· 尽管依赖性被绑定到从查询中返回的行上;但是,他并不被查询中的单个列加以过滤。假如您有一个查询—他返回您的组织的基本成员姓名连同那些单个改变之一的地址(但是,其姓名并不改变),这将触发一个改变通知。很希望,这种特别行为在未来的版本中会有所改变。
· 通知被返回,通过一个专门针对这一目的建立的SqlConnection。这个连接并不加入连接池中。
三、何时使用查询通知
查询通知是针对于并不经常改变的数据而设计的。最好把他应用于服务器端的应用程式(例如ASP.NET或remoting)而不是客户端应用程式(例如Windows表单应用程式)。记住,每一个通知请求都要在SQL Server中注册。假如您拥有大量的都有通知请求的客户端应用程式,那么这可能会导致您的服务器产生资源问题。微软推荐,对于客户端应用程式,您应该限制查询通知使用为不多于十个并行用户。
对于大规模应用程式来说,查询通知可能是一种强有力的帮助,而不用简单地添加越来越多的服务器以满足需要。设想,有一家大型的为成千上百万用户提供在线软件更新服务的软件公司。不是使每一个用户的更新操作都触发服务器上的另一个查询来确定需要哪些组件,而是能够缓冲查询结果并且能够直接从该缓存中服务匹配的查询。
注意:对于客户端应用程式来说,应该限制您的查询通知使用—不多于十个并发用户。
对于较小规模的情况而言,下拉式列表框是另一种典型的数据集;此时该数据集更新的次数并不如请求的次数多。产品列表、州列表、国家列表、供给商、销售人,甚至更多不太需要频繁改变的信息正是使用通知的较好候选。
四、为使用查询通知作准备
因为默认情况下SQL Server 2005处于高度安全的状态,所以您需要“打开”一些功能才能使用查询通知。首先,您要使用的每一个数据库都需要启动Service Broker功能。为此,您能够在T-SQL中使用如下命令实现:
USE mydatabase
ALTER DATABASE mydb SET ENABLE_BROKER
另外,您需要授予一些SQL Server权限以允许非管理员帐户能够参和使用查询通知。
五、SqlDependency.Start和Stop
SqlDependency 和SqlCacheDependency都需要,在任何通知请求前先调用静态方法SqlDependency.Start()。这个方法负责创建一个SqlConnection以实现在数据改变时接收通知。注意,您仅需要在一个应用程式的生命周期的开始建立这些内容。例如,在一个ASP.NET应用程式中,global.asax文档的Application_Start事件处理器就是实现这一功能的好地方。
注意,对包含在通知中的每一个连接都应该调用Start方法。因此,假如您在应用程式中存取多个数据库,那么您需要为每一个数据库调用Start。在下列示例中,有一个针对Pubs数据库的连接串pubsConn,他在这个应用程式的web.config文档中定义。
为了切断这个连接,您能够使用SqlDependency.Stop(),这也是个静态方法。

Sub Application_Start(ByVal sender as Object
, _
ByVal e as EventArgs)
System.Data.SqlClient.SqlDependency.Start _
(System.Configuration.ConfigurationManager. _
Connectionstrings("pubsConn").ConnectionString)
End Sub
Sub Application_End(ByVal sender as Object,
ByVal e as EventArgs)
System.Data.SqlClient.SqlDependency.Stop _
(System.Configuration.ConfigurationManager. _
Connectionstrings("pubsConn").ConnectionString)
End Sub
假如您在调用Start和Stop的同时观察SQL Server Profiler,那么您会看到许多有趣的信息。当调用Start时,应用程式运行一个查询以确保支持Service Broker,然后创建一个存储过程备以后用于清除在Service Broker基础结构中的SqlDependency队列和服务。最后,他运行一个SQL Server 2005 WaitFor命令,该命令负责查询在Notification Service部分的入口。这就是假如您使用ADO.NET的低级SqlNotificationRequest对象的话任何您需要显式完成的事情。
在整个的.NET 2.0的设计过程中,SqlDependency底层架构从一种推模式(来自SQL Server)改变为一种拉模式(来自.NET)。这样做的原因是为了解决第一次设计时所导致的一些安全问题。微软的Sushil Chordia在MSDN上发表了一篇有关于这种改进的文章,该文周详描述了这一改进的内在机理。
六、您的第一个通知
下面,让我们开始使用SqlDependency来分析一下任何上面这些是如何协同工作的。
首先,我们创建一个类NotificationTest来存取您的数据。在这个类中,还要创建一个典型的函数以便从Pubs数据库的Authors表中查询一些数据并返回一个SqlDataReader。

Imports System.Data.SqlClient
Public Class NotificationTest
Public Function DepTest() As SqlDataReader
Dim conn As New SqlConnection(connstring)
conn.Open()
Dim cmd As New SqlCommand(
"SELECT * FROM authors("
, conn)")
Dim rdr As SqlDataReader
rdr = cmd.ExecuteReader()
Return rdr
End Function
End Class
现在,让我们修改代码来加入这种依赖性。首先,声明一个名为SqlDependency的对象。为了使之用于该类中的其他函数中,我把他定义为一个类变量。
然后,您需要改变这个查询。查询通知需要您显式地列举在您的查询中的列,连同总是使用一种“两部分”的表名。注意一下在修改后的代码示例中的新的查询文本。
然后,实例化新的SqlDependency并且把他依附到命令中。
就是这些。当执行命令时,依赖性随着他直到数据库。在他处理查询的同时,SQL Server能够看到这一依赖性并且把他发送到Service Broker以注册他。

Imports System.Data.SqlClient
Public Class NotificationTest
Dim dep As SqlDependency
Public Function DepTest() As SqlDataReader
Dim conn As New SqlConnection(connstring)
conn.Open()
Dim cmd As New SqlCommand( _
"SELECT au_id
, au_lname,au_fname " & _
"FROM dbo.authors", conn)
dep = New SqlDependency(cmd)
Dim rdr As SqlDataReader
rdr = cmd.ExecuteReader()
Return rdr
End Function
End Class
现在,您已注册了依赖性,但是当通知返回到应用程式时您还根本没有捕获他。但是,SqlDependency类提供了两种方式来了解一个通知。一种方式是通过OnChange事件,您能够通过创建一个代理来捕获他;另一种方式是通过属性HasChanges,您能够在您的应用程式逻辑中对之进行测试。在下列代码中,我在OnDepChange事件中添加了代码以便在后面的某个时候测试通知。
 
Imports System.Data.SqlClient
Public Class NotificationTest
Dim dep As SqlDependency
Public Function DepTest() As SqlDataReader
Dim conn As New SqlConnection(connstring)
conn.Open()
Dim cmd As New SqlCommand( _
"SELECT au_id
,au_lname,au_fname FROM " + _
"dbo.authors", conn)
dep = New SqlDependency(cmd)
AddHandler dep.OnChange, AddressOf OnDepChange
Dim rdr As SqlDataReader
rdr = cmd.ExecuteReader()
Return rdr
End Function
'处理器方法
Public Sub OnDepChange(ByVal sender As Object, _
ByVal e As SqlNotificationEventArgs)
Dim DepInfo As String = e.Info.ToString
'做一些事情以响应通知
End Sub
Public ReadOnly Property HasChanges() As Boolean
Get
Return dep.HasChanges
End Get
End Property
End Class
现在,我们来看一下其工作原理。首先,把一个断点放到OnDepChange事件的End Sub代码行。然后,从您喜欢的网页、表单程式或控制台程式中调用DepTest函数来进行测试。在返回SqlDataReader后,在Visual Studio 2005的Server Explorer或在SQL Server Management Studio中打开Authors表并且编辑某一个字段内容。例如,一旦锁定这一改变,那么,当您把光标移动到表中的一个新行时,断点应该被激活。
七、SQLNotificationEventArgs
当您看到通知的确从数据库中传来时,您能够分析一下相应变量的值,他是个SqlNotificationEventArgs对象。SqlDependency总是随着OnChange事件返回这个对象,而且他是很有用的。其中,SqlNotificationInfo是个具备18种可能值的枚举类型。其中,一些值对应情况正常,而另一些显示出了问题。这些枚举中有Update,Insert和Delete—告诉您在数据中发生了什么类型的变化。更有其他一些值即使在事件发生时也不会被发送。例如,重新启动服务器将激发任何的通知;而枚举值Drop或Truncate告诉您已对依赖的表实现了某种操作。
另外,还存在一些依赖性甚至还不能被注册的情形,例如假如您试图对一个UPDATE查询配置一个依赖性将返回Invalid。而返回值Query显示您的查询语法并不符合通知的严格规则。上面枚举表中的最后两个枚举值,更有其他几个和不能注册查询相关的枚举值在执行该命令时被立即返回。
通过查找MSDN库中的有关SqlNotificationInfo枚举文档,您能够得到这些枚举的完全列表。
当我一些场合上谈论查询通知时,人们总是问我:“通知是否会告诉您发生了什么事情?”。回答是“不会”。
总之,SQLNotificationEventArgs能够向您给出一个通知中最为周详的信息,而这些信息在调试排错时是很有用的。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值