淺析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.pswp.cn/news/274113.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274113.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274113.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android 第十二課 使用LitePal操作數據庫(記得閱讀最后面的注意事項哦)

一、LitePal簡介 1、(新建項目LitePalTest)正式接觸第一個開源庫---LitePalLitePal是一款開源的Android 數據庫框架,它采用了對象關系映射(ORM)的模式。2、配置LitePal,編輯app/build.gradle文件,在dependencies閉包中…

listview頻繁刷新報錯

在Android編程中使用Adapter時,偶爾會出現如下錯誤:The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI t…

Android 第十三課 SharedPreferences存儲

SharedPreferences是使用鍵值對的方式來存儲數據的。當保存一條數據時,需要給這條數據提供一個對應的鍵,這樣在讀取數據的時候就可以通過這個鍵把相應的值取出來。而且支SharedPreferences還支持多種不同的數據類型存儲,例如:如果…

DSP的Gel作用

轉自:http://blog.csdn.net/azhgul/article/details/6660960最近剛在研究Davinci系,特此MARK下,以資后續學習之用。 DSP的Gel作用 1 GEL文件基本作用 當CCSStudio啟動時,GEL文件加載到PC機的內存中,如果定義了StartUp(…

解決關于登錄校園網顯示不在IP段的問題方案(要看注意事項哦!)

有時,登錄校園網,賬號和密碼都顯示正確,但是卻顯示出“賬號只能在指定IP段登錄”的問題。 那我們就提供了一個解決方案: 使用WinR,并在輸入框,輸入cmd命令:(如下)接著輸入&#xff1…

jquery插件編寫

jQuery為開發插件提拱了兩個方法,分別是: jQuery.fn.extend(object); jQuery.extend(object); jQuery.extend(object); 為擴展jQuery類本身.為類添加新的方法。可以理解為添加靜態方法。是全局的(位于jQuery命名空間內部的函數)…

gtk/Glade編程 編譯命令不成功 解決方法

摘自:http://blog.chinaunix.net/uid-26746982-id-3433656.html 當我們編寫gtk/glade程序,gcc編譯時,用如下命令: #gcc -o server server.c pkg-config --cflags --libs gtk-2.0 報錯:/tmp/ccoXadAd.o: In function …

Android 第十五課 如何使用LitePal從SQLite數據庫中刪除數據(十四課用來保留講解如何向SQLite數據庫中存入數據)

使用LitePal刪除數據的方式主要有兩種,第一種就是直接調用已存對象的delete()方法,所謂已存儲對象就是調用過save()方法的對象,或者說是通過LitePal提供的查詢API查出來的對象,都是可以直接使用delete方法來刪除對象的。這是比較簡…

頁面返回頂部(方法比較)

下面就說下簡單的返回頂部效果的代碼實現&#xff0c;附注釋說明。 1. 最簡單的靜態返回頂部&#xff0c;點擊直接跳轉頁面頂部&#xff0c;常見于固定放置在頁面底部返回頂部功能 方法一&#xff1a;用命名錨點擊返回到頂部預設的id為top的元素 html代碼 <a href"#top…

Android 第十六課 使用LitePal查詢數據

LitePal在查詢API方面做了非常多的優化&#xff0c;基本上可以滿足絕大多數場景的查詢需求&#xff0c;并且代碼也十分整潔。 例如我們需要查詢表中的所有數據&#xff1a; List<books> DataSupport.findAll(Book.class); 沒有冗長的參數列表&#xff0c;只需要調用一下…

linux創建桌面圖標,和開始菜單欄圖標

轉自&#xff1a;http://blog.csdn.net/qq_25773973/article/details/50514767 ###環境&#xff1a;Mint17&#xff0c;&#xff08;其他類似的linux系統是一樣的&#xff09; 如果開始菜單有圖標&#xff0c;創建桌面圖標很簡單&#xff0c;右鍵添加到桌面即可。 如果沒有&am…

ScrollView中使用ListView

轉自 http://blog.csdn.net/fzh0803/article/details/7971391 由于scrollview和listview不能直接共存&#xff0c;在scrollview中直接使用lsitview的話只會顯示一個條目&#xff0c;要使他們共存&#xff0c; 據我所知&#xff0c;有三種方法&#xff1a; 1。如果listview的高度…

Android 第十四課 使用LitePal添加數據(更新數據)

我們注意到當你登錄一個app&#xff0c;是不是需要先注冊呢&#xff1f;&#xff0c;所謂注冊&#xff0c;簡單地來理解是不是就是把輸入框中地數據傳入數據庫中呢&#xff1f; 這里我們設置簡單一點&#xff0c;注冊的信息只包括兩項&#xff0c;一項是用戶名&#xff0c;另一…

微信公眾平臺的服務號和訂閱號

微信公眾平臺 服務號 訂閱號 作者&#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;再將成果記錄及分享之&…

Python第一課

對python仰慕已久&#xff0c;今日下定決心學習。可能我是一時頭腦發熱&#xff0c;但我還是愿意堅持。 先了解一下&#xff1a;命令行模式和Python交互模式 在Windows開始菜單選擇“命令提示符”&#xff0c;就進入到命令行模式&#xff0c;它的提示符類似C:\>&#xff1a;…

C++模板專門化與重載

最近在復習C有關知識&#xff0c;又重新看<<Effective C>>&#xff0c;收獲頗豐。原來以前看這邊書&#xff0c;好多地方都是淺嘗輒止。<<Effective C>>條款25&#xff1a;考慮寫出一個不拋出異常的swap函數&#xff0c;涉及到C模板專門化(Templates S…

Android 第十八課 強大的滾動控件 RecyclerView

步驟&#xff1a; 一、添加依賴庫compilecom.android.support:recyclerview-v7:26.1.0 二、在activity_mian.xml中&#xff0c;添加RecyclerView控件&#xff0c;并占據整個頁面。 三、把你要在RecyclerView中展示的內容&#xff0c;設置成一個實體類Fruit&#xff0c;接著為Re…