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

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:
摘要 在本系列文章中,我们将深入探讨如何把.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)。

剖析SQLServer2005查询通知之基础篇
图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能够向你给出一个通知中最为详细的信息,而这些信息在调试排错时是非常有用的。
















本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/59884,如需转载请自行联系原作者



相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
打赏
0
0
0
0
143
分享
相关文章
SQL查询太慢?实战讲解YashanDB SQL调优思路
本文是Meetup第十期“调优实战专场”的第二篇技术文章,上一篇《高效查询秘诀,解码YashanDB优化器分组查询优化手段》中,我们揭秘了YashanDB分组查询优化秘诀,本文将通过一个案例,助你快速上手YashanDB慢日志功能,精准定位“慢SQL”后进行优化。
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
OmniSQL:开源文本到SQL神器!自然语言秒转查询到复杂多表连接等SQL需求
OmniSQL是开源的文本到SQL转换模型,通过创新的数据合成框架生成250万条高质量样本,支持7B/14B/32B三种模型版本,能处理从简单查询到复杂多表连接等各种SQL需求。
139 16
OmniSQL:开源文本到SQL神器!自然语言秒转查询到复杂多表连接等SQL需求
玩转大数据:从零开始掌握SQL查询基础
玩转大数据:从零开始掌握SQL查询基础
129 35
如何优化SQL查询以提高数据库性能?
这篇文章以生动的比喻介绍了优化SQL查询的重要性及方法。它首先将未优化的SQL查询比作在自助餐厅贪多嚼不烂的行为,强调了只获取必要数据的必要性。接着,文章详细讲解了四种优化策略:**精简选择**(避免使用`SELECT *`)、**专业筛选**(利用`WHERE`缩小范围)、**高效联接**(索引和限制数据量)以及**使用索引**(加速搜索)。此外,还探讨了如何避免N+1查询问题、使用分页限制结果、理解执行计划以及定期维护数据库健康。通过这些技巧,可以显著提升数据库性能,让查询更高效流畅。
如何在Django中正确使用参数化查询或ORM来避免SQL注入漏洞?
如何在Django中正确使用参数化查询或ORM来避免SQL注入漏洞?
210 77
利用 PolarDB PG 版向量化引擎,加速复杂 SQL 查询!完成任务领发财新年抱枕!
利用 PolarDB PG 版向量化引擎,加速复杂 SQL 查询!完成任务领发财新年抱枕!
105 14
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
69 1
SQL为什么不建议执行多表关联查询
本文探讨了SQL中不建议执行多表关联查询的原因,特别是MySQL与PG在多表关联上的区别。MySQL仅支持嵌套循环连接,而不支持排序-合并连接和散列连接,因此在多表(超过3张)关联查询时效率较低。文章还分析了多表关联查询与多次单表查询的效率对比,指出将关联操作放在Service层处理的优势,包括减少数据库计算资源消耗、提高缓存效率、降低锁竞争以及更易于分布式扩展等。最后,通过实例展示了如何分解关联查询以优化性能。
|
3月前
|
Java使用sql查询mongodb
通过MongoDB Atlas Data Lake或Apache Drill,可以在Java中使用SQL语法查询MongoDB数据。这两种方法都需要适当的配置和依赖库的支持。希望本文提供的示例和说明能够帮助开发者实现这一目标。
83 17

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等