浅析SQL Server 2005中的主动式通知机制

一、引言

在开发多人同时访问的Web应用程序(其实不只这类程序)时,开发人员往往会在缓存策略的设计上狠下功夫。这是因为,如果将这种环境下不常变更的数据临时存放在应用程序服务器或是用户机器上的话,可以避免频繁地往返访问数据库—而数据库访问是要符出昂贵代价的。以往在低版本的SQL Server(SQL Server 2000及以前版本)中,当需要提供数据库内他人更新后的状况时,主要是通过轮询数据库机制来提供对数据库的不断查询;也可能是借助于存储于数据库表格中的触发器或者通过消息队列方式来达到通知目的。如今,作为微软.NET 2.0战略的重要组成部分之一的SQL Server 2005首次引入了主动式通知(Query Notification)机制。SQL Server 2005在所使用数据更改时,会主动地通知你。这种新的设计模式会让你在系统数据未更新时,减轻浪费网络来回轮询的负担,从而有可能极大地提高系统性能。

本文中,我想通过一个简单的Windows桌面表单示例(基于SQL Server 2005的范例数据库AdventureWorks)向读者展示SQL Server 2005中这种新的主动地通知工作机理。

【另注】由于Visual Studio 2005的革命性变化,你可以极为容易地把这个例子更改到Web应用程序场合下。

二、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中检索回通知。

【作者注】Service Broker是SQL Server 2005中新增加的一项重要服务,旨在为日趋流行的面向服务的架构(Service-Oriented Architecture,即“SOA”)在数据库存储级提供基础性支持。

其实,完整的通知架构还是比较复杂的。其中参与的组件可能包括:SQL Server 2005查询引擎、Service Broker、系统存储过程sp_DispatcherProc;ADO.NET的SqlNotification类(System.Data.Sql.SqlNotificationRequest)、SqlDependency类(System.Data.Sql.SqlDependency);以及ASP.NET 2.0中新的Cache类(System.Web.Caching.Cache)等等。

下图1展示了SQL Server 2005中的主动通知机制及其与客户端ASP.NET页面交互的示意图。

 

图1:SQL Server 2005主动通知机制示意图

上面的运行逻辑大致如下:

(1)SqlCommand类中提供了一个Notification属性,用于存储通知相关的设置。当SqlCommand执行时,会让传递该执行需求的TDS协议附加上通知的相应信息。
(2)SQL Server 2005收到该需求后,为这个需求注册通知,并执行该需求自身的SQL语句;
(3)接下来,SQL Server 2005会监控后续执行的DML语法,并确定是否能够影响前一步返回给前端的数据集;一旦有影响,则会立即发送一个消息到Service Broker;
(4)Service Broker的队列中有消息后,可能发生如下情况:
  a)Notification在前端应用程序侦听的队列中放入消息,由ADO.NET的下层自动读取消息并触发事件;
  b)在Service Broker内的消息持续保留着,较高级的前端应用程序会自己处理这个消息。

#p#

如前所述,由于SQL Server 2005的通知机制在基层上依赖于Services Broker,所以要发出通知的数据库必须让Services Broker启动。Services Broker利用SQL Server 2005所提供的队列创建异步通知。而通知其实就是一组Services Broker内置好的服务(也即是标准的消息、发送的消息及发送消息的规则等等)。下图2中,我们通过SQL Server Management Studio中的对象资源管理器窗口查看每一个范例数据库AdventureWorks的“Services Broker”节点下属相关的设置情况:

 

图2:SQL Server 2005在Services Broker中已经准备好主动式通知设置情况

前面已经提到,我们想通过SQL Server 2005的范例数据库AdventureWorks进行试验;所以,若要让程序能够收到通知,必须先启动该数据库的相应服务,同时还要允许登录的帐户订阅这种查询通知。下面SQL语句实现创建相应的设置:

--启动Service Broker服务支持

ALTER DATABASE AdventureWorks SET ENABLE_BROKER
--
--【提示】我们无法直接在sp_dboption中(使用“EXEC sp_dboption AdventureWorks”语句)
--看出某个数据库是否启动了Service Broker服务
--需要观察sys.databases的is_broker_enabled字段才知道是否已经启动—使用如下语句:

SELECT * FROM sys.databases

--允许某个账号订阅查询
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [YourComputerName\UserName]

三、示例分析

有了上面的分析和相应的SQL设置后,现在让我们来观察一个使用SQL Server 2005主动式通知机制的Windows桌面应用程序的示例。程序相应表单的设计界面如下图3所示:

 

图3:表单的设计界面

Public Class DeskNotification

    Dim conn As New SqlConnection(ADONET20.My.Settings.AdventureWorksConnection)
    Delegate Sub PopulateList()
    Private Sub DeskNotification_Load(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles MyBase.Load
        SqlDependency.Start(ADONET20.My.Settings.AdventureWorksConnection)
        ‘取得初始数据
        ListProducts()
    End Sub

    Private Sub productListBox_SelectedIndexChanged(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles productListBox.SelectedIndexChanged
        Dim strItem As String = productListBox.SelectedItem.ToString
        lblId.Text = strItem.Substring(0, strItem.IndexOf("-") - 1)
        txtPrice.Text = strItem.Substring(strItem.IndexOf(":") + 1)
    End Sub

    Private Sub btnUpdate_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles btnUpdate.Click
        Dim cnn As New SqlConnection(ADONET20.My.Settings.AdventureWorksConnection)
        If lblId.Text = "无" Then
            MessageBox.Show("请选择某一条记录")
            Exit Sub
        End If
        cnn.Open()
        Dim cmd As New SqlCommand( _
        "UPDATE Production.Product SET ListPrice=" & txtPrice.Text & " WHERE

ProductID=" & lblId.Text, _
        cnn)
        cmd.ExecuteNonQuery()
        cnn.Close()

    End Sub
    Sub OnDependencyChanged(ByVal sender As Object, ByVal e As SqlNotificationEventArgs)
        'SqlDependency对象的OnChanged事件触发时
        '要执行的业务逻辑
        Dim dR As DialogResult
        dR = MessageBox.Show("数据已经完毕,要更新数据吗?", e.Info.ToString, _
        MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If dR = Windows.Forms.DialogResult.Yes Then
            '由表单的主线程实现数据更新
            Try
                Me.Invoke(New PopulateList(AddressOf ListProducts))
            Catch ex As Exception
                MessageBox.Show(ex.Message)
            End Try
        End If
    End Sub

    Public Sub ListProducts()
        '重新装载数据
        ' SqlDependency设置后,仅会注册一次的事件通知
        Dim dep As New SqlDependency()
        '设置SqlDependency对象的OnChanged事件发生时要调用哪个事件处理器
        AddHandler dep.OnChange, AddressOf OnDependencyChanged
        '限制查询的范围,避免太大的范围的大量用户都影响到这个范围内的数据以致使SQL Server
        '频繁地触发通知
        Using cmd As New SqlCommand( _
        "SELECT ProductID, Name, ListPrice FROM Production.Product " & _
        "WHERE ProductID BETWEEN @Start AND @End", conn)
            With cmd
                .Parameters.Add(New SqlParameter("@Start", Data.SqlDbType.Int))
                .Parameters.Add(New SqlParameter("@End", Data.SqlDbType.Int))
                .Parameters(0).Value = txtStart.Text
                .Parameters(1).Value = txtEnd.Text
            End With

            '自动帮助我们设置SqlCommand的Notification属性所需的SqlNotificationRequest对象
            '可以通过Debug来观察SqlCommand对象执行前后的关系
            dep.AddCommandDependency(cmd)
            productListBox.Items.Clear()
            conn.Open()
            Dim reader As SqlDataReader = cmd.ExecuteReader()
            While reader.Read()
                productListBox.Items.Add(reader("ProductID") & " - " & _
                reader("Name").ToString & ": " & reader("ListPrice").ToString)
            End While
        End Using
        conn.Close()
    End Sub

    Private Sub DeskNotification_FormClosing(ByVal sender As System.Object,

ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        SqlDependency.Stop(ADONET20.My.Settings.AdventureWorksConnection)

    End Sub
End Class

#p#

在这个例子中,我们首先在Global.asax文件内的Application_Start事件加入通过SqlDependency类的静态方法Start启动监听。注意,这个Start方法需要传递数据库连接字符串。它将完成如下相应操作:

◆打开一条新的不经过数据库连接池的到SQL Server 2005的连接;
◆在服务器上创建一个新的队列,并赋予唯一名称;
◆在该队列上创建一个唯一名称的服务;
◆在服务器上创建一个新的存储过程,在客户端不再监听队列时,清除掉上述的临时创建的各种对象;
◆侦听队列所收到的更改通知。

【注意】在上面的例子中,尽管你可以通过SqlCommand实例中的SQL更改记录,但并没有自动更新列表框内的记录数据,而是在收到SQL Server记录改变的通知后,通过事件触发指到OnDenpedencyChanged函数调用主线程重新执行ListProducts方法,从相关数据表读取更新后的记录来重设列表框的内容。

四、何时使用主动式通知机制

查询通知是针对于并不经常改变的数据而设计的。最好把它应用于服务器端的应用程序(例如ASP.NET或remoting)而不是客户端应用程序(例如Windows表单应用程序)。记住,每一个通知请求都要在SQL Server中注册。如果你拥有大量的都有通知请求的客户端应用程序,那么这可能会导致你的服务器产生资源问题。鉴于此,微软推荐,对于客户端应用程序,你应该限制使用查询通知的最大并行用户数不多于十个。

对于大规模应用程序来说,查询通知可能是一种强有力的帮助,而不用简单地添加越来越多的服务器以满足要求。设想,有一家大型的为成千上百万用户提供在线软件更新服务的软件公司。不是使每一个用户的更新操作都触发服务器上的另一个查询来确定需要哪些组件,而是能够缓冲查询结果并且可以直接从该缓存中服务匹配的查询。

对于较小规模的情况而言,下拉式列表框是另一种典型的数据集;此时该数据集更新的次数一般不如请求的次数多。产品列表、州列表、国家列表、供应商、销售人,甚至更多不太需要频繁改变的信息正是使用上述通知机制的较好候选。

五、小结

尽管查询通知是.NET 2.0中最重要的特征之一,但是目前它仍然难与其它优秀特征(例如ASP.NET中的泛型或UI魔术等)相衔接。然而,无论你使用它来防止针对于含有数百个项的下拉列表框的连续的反复查询,还是使用它来管理基于Web的上百万的客户端计算机的更新,它都能有效地帮助你减少资源开支。就其最简单的应用来看,借助于ASP.NET OutputCache指令(或通过在你的Web应用程序的中间层或Web服务中构建一种复杂缓冲的机制),查询通知可以成为创建可扩展的具有响应性的应用程序的强有力的协作开发工具。

【另注】本文基于SQL Server 2005 Express Edition调试通过。另外,尽管查询通知可以与SQL Server Express(SSE)一起使用,但是SSE数据库必须是一个命名的实例(命名的实例是安装选项之一)。

转载于:https://www.cnblogs.com/amylis_chen/p/3234594.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 第十二课 使用LitePal操作数据库(记得阅读最后面的注意事项哦)

一、LitePal简介 1、(新建项目LitePalTest)正式接触第一个开源库---LitePalLitePal是一款开源的Android 数据库框架,它采用了对象关系映射(ORM)的模式。2、配置LitePal,编辑app/build.gradle文件,在dependencies闭包中…

解决关于登录校园网显示不在IP段的问题方案(要看注意事项哦!)

有时,登录校园网,账号和密码都显示正确,但是却显示出“账号只能在指定IP段登录”的问题。 那我们就提供了一个解决方案: 使用WinR,并在输入框,输入cmd命令:(如下)接着输入&#xff1…

页面返回顶部(方法比较)

下面就说下简单的返回顶部效果的代码实现&#xff0c;附注释说明。 1. 最简单的静态返回顶部&#xff0c;点击直接跳转页面顶部&#xff0c;常见于固定放置在页面底部返回顶部功能 方法一&#xff1a;用命名锚点击返回到顶部预设的id为top的元素 html代码 <a href"#top…

微信公众平台的服务号和订阅号

微信公众平台 服务号 订阅号 作者&#xff1a;方倍工作室 地址&#xff1a;http://www.cnblogs.com/txw1958/p/ServiceNumber-subscriptionNumber.html 什么是服务号&#xff1f; 服务号给企业和组织提供更强大的业务服务与用户管理能力&#xff0c;帮助企业快速实现全新的公众…

Android 第十七课 碎片的简单用法及动态添加碎片

Fragment(碎片)是一种可以嵌入在活动当中的UI片段&#xff0c;它可以让程序更加合理和充分的利用大屏幕的空间。碎片和活动太像了&#xff0c;同样都包含布局&#xff0c;都有自己的声明周期&#xff0c;可以将碎片理解为一种迷你型的活动。 新建FragmentTest项目。假设项目已经…

在Linux下禁用键盘、鼠标、触摸板(笔记本)等输入设备

在Linux系统下禁用键盘、触摸板、鼠标等输入设备&#xff0c;可以通过xinput命令来实现&#xff1a;主要涉及&#xff1a;#xinput list#xinput list-props list-number#xinput set-prop list-number func-number 1/0具体操作如下&#xff1a;step1&#xff1a;查看系统中有那些…

委托又给我惹麻烦了————记委托链的取消注册、获取返回值

今天改bug碰到了一个问题&#xff0c;有多个方法注册到了一个事件里去&#xff0c;而这些方法本身又有点儿互斥&#xff0c;因而造成了bug&#xff0c;哥调试半天才发现&#xff0c;郁闷至极&#xff0c;遂复习了以前的知识并进行适当延伸&#xff0c;再将成果记录及分享之&…

Android 第十八课 强大的滚动控件 RecyclerView

步骤&#xff1a; 一、添加依赖库compilecom.android.support:recyclerview-v7:26.1.0 二、在activity_mian.xml中&#xff0c;添加RecyclerView控件&#xff0c;并占据整个页面。 三、把你要在RecyclerView中展示的内容&#xff0c;设置成一个实体类Fruit&#xff0c;接着为Re…

ios即时通讯客户端开发之-mac上安装MySQL

一、安装 到MySQL官网上http://dev.mysql.com/downloads/mysql/&#xff0c;下载mysql可安装dmg版本 比如&#xff1a;Mac OS X ver. 10.7 (x86, 64-bit), DMG Archive 下载完的文件为&#xff1a;mysql-5.6.10-osx10.7-x86_64.dmg 1.点击&#xff0c;安装包里的 2.点击安装 安…

dbus 和 policykit 实例篇(python)

dbus 和 policykit 实例篇&#xff08;python&#xff09; 使用policykit 的程序一般都有一个dbus daemon程序来完成相关操作&#xff0c;这个dbus daemon 会在系统注册一个system bus 服务名&#xff0c;用于响应要求root privileged的操作&#xff0c;当dbus请求到达时会先验…

和菜鸟一起学linux之DBUS基础学习记录

转自&#xff1a;http://blog.csdn.net/eastmoon502136/article/details/10044993 D-Bus三层架构 D-Bus是一个为应用程序间通信的消息总线系统, 用于进程之间的通信。它是个3层架构的IPC 系统&#xff0c;包括&#xff1a; 1、函数库libdbus &#xff0c;用于两个应用程序互…

Android 第二十课 广播机制(大喇叭)----发送自定义广播(包括发送标准广播和发送有序广播)

广播分为两种类型&#xff1a;标准广播和有序广播 我们来看一下具体这两者的具体区别&#xff1a; 1、发送标准广播 我们需要先定义一个广播接收器来准备接收此广播才行&#xff0c;否则也是白发。 新建一个MyBroadcastReceiver,代码如下&#xff1a; package com.example.broa…

八大排序算法

概述 排序有内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 我们这里说说八大排序就是内部排序。 当n较大&#xff0c;则应采用…

Android 第二十一课 RecyclerView简单的应用之编写“精美”的聊天页面

1、由于我们会使用到RecyclerView&#xff0c;因此首先需要在app/build.gradle当中添加依赖库。如下&#xff1a; apply plugin: com.android.application .... dependencies {....compile com.android.support:recyclerview-v7:26.1.0 } 2、然后开始编写主页面&#xff0c;修该…

JavaScript 第四课 案例研究:JavaScript图片库

主要内容&#xff1a;编写一个优秀的标记文件编写一个JavaScript函数以显示用户想要查看的内容由标记出发函数调用使用几个新方法扩展这个JavaScript函数 学习过DOM&#xff0c;我们用JavaScript和DOM去建立一个图片库。最好的办法是什么呢&#xff1f; 利用JavaScript来建立图…

windows下mongodb安装与使用整理

一、首先安装mongodb 1.下载地址&#xff1a;http://www.mongodb.org/downloads 2.解压缩到自己想要安装的目录&#xff0c;比如d:\mongodb 3.创建文件夹d:\mongodb\data\db、d:\mongodb\data\log&#xff0c;分别用来安装db和日志文件&#xff0c;在log文件夹下创建一个日志文…

USACO4.12Beef McNuggets(背包+数论)

昨天晚上写的一题 结果USACO一直挂中 今天交了下 有一点点的数论知识 背包很好想 就是不好确定上界 官方题解&#xff1a; 这是一个背包问题。一般使用动态规划求解。 一种具体的实现是&#xff1a;用一个线性表储存所有的节点是否可以相加得到的状态&#xff0c;然后每次可以…

Java 循环语句中 break,continue,return有什么区别?

break 结束循环&#xff0c;跳出循环体,进行后面的程序;continue 结束本次循环&#xff0c;进行下次循环;return 跳出循环体所在的方法&#xff0c;相当于结束该方法; 例子&#xff1a; public class whiletrueTest{public static void main(String[] args) {heihei();haha();…

运算放大器单电源应用中的使用齐纳二极管偏置方法

运算放大器单电源应用中的偏置方法除了使用大电阻使运放输出达到电源电压的一半外&#xff0c;还有使用齐纳二极管&#xff08;稳压管&#xff09;方法也能得到达到应用目的。 下面就推荐几个齐纳二极管&#xff08;分别对应着电源电压是15V,12V&#xff0c;9V;5V&#xff09; …

dpi 、 dip 、分辨率、屏幕尺寸、px、density 关系以及换算

本文转自&#xff1a;http://www.cnblogs.com/yaozhongxiao/archive/2014/07/14/3842908.html 一、基本概念 dip &#xff1a; Density independent pixels &#xff0c;设备无关像素。 dp &#xff1a;就是dip px &#xff1a; 像素 dpi &#xf…