SQL Server 2008中Service Broker基础应用(上)
2013-05-14 14:39
295 查看
SQL Server 2008中Service Broker基础应用(上)
导读:本文主要涉及Service Broker的基本概念及建立一个Service Broker应用程序的基本步骤。一、前言:
Service Broker为SQL Server提供消息队列,这提供了从数据库中发送异步事务性消息队列的方法。Service Broker消息可以保证以适当的顺序或原始的发送顺序不重复地一次性接收。并且因为内建在SQL Server中,这些消息在数据库发生故障时是可以恢复的,也可以随数据库一起备份。在SQL Server 2008中,还引入了使用Create Broker Priority命令对会话设定优先级,可以对重要的或不重要的会话进行优先级设定,以保证消息合理地处理。
本文假定一个在线数据库BookStore中存储了一些业务订单。我们使用Service Broker应用程序将消息发送到另一个数据库BookDistribution,该数据库是分离的应用程序调用,该应用程序控制仓库入库和出库交付, 并返回消息给BookStore。
创建Service Broker应用程序大体步骤如下:
1、定义希望应用程序执行的异步任务。
2、确定Service Broker的发起方服务和目标服务是否创建在同一个SQL Server实例中。如果是两个实例,实例间的通信还需要创建经过证书认证或NT安全的身份认证,并且要创建端点、路由以及对话安全模式。
3、如果没有启用,则在多方参与的数据库中使用Alter Database命令设置Enable_broker以及Truseworthy数据库选项。
4、为所有多方参与的数据库创建数据库主密钥。
5、创建希望在服务之间发送的消息类型。
6、创建契约(Contract)来定义可以由发起方发送的各种消息以及由目标发送的消息类型的种类。
7、同时在两方参与的数据库中创建用于保存消息的队列。
8、同时在绑定特定约定到特定队列的多方参与的数据库中创建服务。
二、实例
下面我们通过一个示例来实现以上步骤:
(一)、启用数据库的Service Broker活动
-- Enabling Databases for Service Broker Activity USE master GO IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'BookStore') CREATE DATABASE BookStore GO IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'BookDistribution') CREATE DATABASE BookDistribution GO ALTER DATABASE BookStore SET ENABLE_BROKER GO ALTER DATABASE BookStore SET TRUSTWORTHY ON GO ALTER DATABASE BookDistribution SET ENABLE_BROKER GO ALTER DATABASE BookDistribution SET TRUSTWORTHY ON
(二)、创建数据库主密钥
-- Creating the DatabaseMaster Key for Encryption USE BookStore GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'I5Q7w1d3' GO USE BookDistribution GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'D1J3q5z8X6y4' GO
(三)、管理消息类型
使用CREATE MESSAGE TYPE(http://msdn.microsoft.com/en-us/library/ms187744.aspx)命令,
-- Managing Message Types Use BookStore GO -- 发送图书订单的消息类型 CREATE MESSAGE TYPE [//SackConsulting/SendBookOrder] VALIDATION = WELL_FORMED_XML GO --目标数据库发送的消息类型 CREATE MESSAGE TYPE [//SackConsulting/BookOrderReceived] VALIDATION = WELL_FORMED_XML GO --执行同样的定义 Use BookDistribution GO -- 发送图书订单的消息类型 CREATE MESSAGE TYPE [//SackConsulting/SendBookOrder] VALIDATION = WELL_FORMED_XML GO --目标数据库发送的消息类型 CREATE MESSAGE TYPE [//SackConsulting/BookOrderReceived] VALIDATION = WELL_FORMED_XML GO
--注意,此处没有定义消息的内容。实际的消息是消息类型的实例。
(四)、创建契约(Contract)
使用Create Contract(http://msdn.microsoft.com/en-us/library/ms178528.aspx)
-- Creating Contracts Use BookStore GO CREATE CONTRACT [//SackConsulting/BookOrderContract] ( [//SackConsulting/SendBookOrder] SENT BY INITIATOR, [//SackConsulting/BookOrderReceived] SENT BY TARGET ) GO USE BookDistribution GO CREATE CONTRACT [//SackConsulting/BookOrderContract] ( [//SackConsulting/SendBookOrder] SENT BY INITIATOR, [//SackConsulting/BookOrderReceived] SENT BY TARGET ) GO
--发起方和目标的定义必须相同
(五)、创建队列
队列用来保存数据。使用命令Create queue(http://msdn.microsoft.com/en-us/library/ms190495.aspx)
-- Creating Queues Use BookStore GO --保存BookDistribution过来的消息 CREATE QUEUE BookStoreQueue WITH STATUS=ON GO USE BookDistribution GO --保存BookStore过来的消息 CREATE QUEUE BookDistributionQueue WITH STATUS=ON GO
(六)、创建服务
服务定义端点,然后使用它来将消息队列绑定到一个或多个契约上。服务使用队列和契约来定义一个或一组任务。有点拗口,是不是?
服务是消息的发起方和接收方强制约定的规则,并将消息路由到正确的序列。
使用Create Service(http://msdn.microsoft.com/en-us/library/ms190332.aspx)命令。
-- Creating Services Use BookStore GO CREATE SERVICE [//SackConsulting/BookOrderService] ON QUEUE dbo.BookStoreQueue--指定的队列绑定到契约 ([//SackConsulting/BookOrderContract]) GO USE BookDistribution GO CREATE SERVICE [//SackConsulting/BookDistributionService] ON QUEUE dbo.BookDistributionQueue--指定的队列绑定到契约 ([//SackConsulting/BookOrderContract]) GO
(七)、启动对话
对话会话(dialog conservation)是在服务之间进行消息交换的操作。
使用Begin Dialog Conversation(http://msdn.microsoft.com/en-us/library/ms187377.aspx)
命令创建新的会话。使用Send(http://msdn.microsoft.com/en-us/library/ms188407.aspx)来发送消息。使用End
Conversation命令(http://msdn.microsoft.com/en-us/library/ms177521.aspx)结束会话。
-- Initiating a Dialog Use BookStore GO --保存会话句柄和订单信息 DECLARE @Conv_Handler uniqueidentifier DECLARE @OrderMsg xml; BEGIN DIALOG CONVERSATION @Conv_Handler--创建会话 FROM SERVICE [//SackConsulting/BookOrderService] TO SERVICE '//SackConsulting/BookDistributionService' ON CONTRACT [//SackConsulting/BookOrderContract]; SET @OrderMsg = '<order id="3439" customer="22" orderdate="2/15/2011"> <LineItem ItemNumber="1" ISBN="1-59059-592-0" Quantity="1" /> </order>'; SEND ON CONVERSATION @Conv_Handler--发送到BookDistribution数据库的队列中 MESSAGE TYPE [//SackConsulting/SendBookOrder] (@OrderMsg);
(八)、查询队列中传入的消息
-- Querying the Queue for IncomingMessages USE BookDistribution GO SELECT message_type_name, CAST(message_body as xml) message, queuing_order, conversation_handle, conversation_group_id FROM dbo.BookDistributionQueue
查询结果:
![](http://kdhlla.bay.livefilestore.com/y1puPBMZUdY5SiMl8bNnbfLVzhaEArlbCIPQ280kc1bZIR7Ucqh5FZuVidgU3ILR4hmdaNu76SsQrh0Mjv6jL6qhJEB4HDnN4O-/2011-4-5%2015-34-16.png?psid=1)
(九)、检索并响应消息
使用Receive语句(http://msdn.microsoft.com/en-us/library/ms186963.aspx)从队列中读取行(消息),也可以删除已经读取的消息。Receive的结果可以填充到常规表中,也可以在局部变量中执行其他操作,或发送到其他service
Broker消息。如果消息是XML数据类型的消息,则可以直接借助TSQL的XQuery来操作。
-- Receiving and Responding to aMessage USE BookDistribution GO --创建一个表存放接收到的订单信息 CREATE TABLE dbo.BookOrderReceived (BookOrderReceivedID int IDENTITY (1,1) NOT NULL, conversation_handle uniqueidentifier NOT NULL, conversation_group_id uniqueidentifier NOT NULL, message_body xml NOT NULL) GO -- 声明变量 DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @TextResponseMsg varchar(8000) DECLARE @ResponseMsg xml DECLARE @OrderID int; --从队列中获取消息,将接收值赋于局部变量 RECEIVE TOP(1) @OrderMsg = message_body,--TOP指定最多一条消息 @Conv_Handler = conversation_handle, @Conv_Group = conversation_group_id FROM dbo.BookDistributionQueue; -- 将变量值插入表中 INSERT dbo.BookOrderReceived (conversation_handle, conversation_group_id, message_body) VALUES (@Conv_Handler,@Conv_Group, @OrderMsg ) -- 使用XQuery进行抽取以响应消息订单 SELECT @OrderID = @OrderMsg.value('(/order/@id)[1]', 'int' ) SELECT @TextResponseMsg = '<orderreceived id= "' + CAST(@OrderID as varchar(10)) + '"/>'; SELECT @ResponseMsg = CAST(@TextResponseMsg as xml); -- 使用既有的会话句柄,发送响应消息到发起方 SEND ON CONVERSATION @Conv_Handler MESSAGE TYPE [//SackConsulting/BookOrderReceived]
(十)、结束会话
-- Ending a Conversation USE BookStore GO -- 创建订单确认表 CREATE TABLE dbo.BookOrderConfirmation (BookOrderConfirmationID int IDENTITY (1,1) NOT NULL, conversation_handle uniqueidentifier NOT NULL, DateReceived datetime NOT NULL DEFAULT GETDATE(), message_body xml NOT NULL) DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @TextResponseMsg varchar(8000); RECEIVE TOP(1) @Conv_Handler = conversation_handle, @OrderMsg = message_body FROM dbo.BookStoreQueue INSERT dbo.BookOrderConfirmation (conversation_handle, message_body) VALUES (@Conv_Handler,@OrderMsg ); END CONVERSATION @Conv_Handler; GO USE BookDistribution GO DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @message_type_name nvarchar(256); RECEIVE TOP(1) @Conv_Handler = conversation_handle, @OrderMsg = message_body, @message_type_name = message_type_name FROM dbo.BookDistributionQueue -- 双方必须都结束会话 IF @message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog' BEGIN END CONVERSATION @Conv_Handler; END
--查询会话状态
SELECT state_desc, conversation_handle FROM sys.conversation_endpoints
![](http://kdhlla.bay.livefilestore.com/y1pJnNqAPEnEujLP2hWUO138lQKEzOaPm6ptS5MYjmXx3YA-r2bdGH6HElWICGVswX5gy1ocjmzo7wa2Uec5eVwaK9G8zVRhbfG/2011-4-5%2016-07-38.png?psid=1)
三、小结
本文通过一个实例演示了一个用来发送图书订单消息分发控制数据库的简单的消息交换应用程序。发起方发送图书订单,发回一个响应,并在两个数据库上使用END Conservation结束会话。现实场景中可以转换为其他消息类型、契约、服务和队列。合理运用Service Broker应用程序的异步特性可以防止因应用程序挂起而导致业务系统产生瓶颈。
![](http://static.blog.csdn.net/images/ico_summary.gif)
摘要视图
![](http://static.blog.csdn.net/images/ico_rss.gif)
订阅
有奖征集活动系列——【HTML5游戏编程之旅】 博客导入工具 【限时优惠】第五届云计算大会社区门票抢购
探究云计算数据中心节能增效之道 CSDN博客第一期云计算最佳博主评选 CSDN博客第二期最佳移动开发博主评选
SQLServer2005提供主动通知Query Notification
完整的通知架构还是比较复杂的。其中参与的组件可能包括: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)等等。
下图展示了SQL Server 2005中的主动通知机制及其与客户端ASP.NET页面交互的示意图。
![](http://hi.csdn.net/attachment/201011/18/0_1290067599HIB6.gif)
上面的运行逻辑大致如下:
(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内的消息持续保留着,较高级的前端应用程序会自己处理这个消息。
在SQLServer2005中,SQL Server Service Broker 用于创建会话以交换消息。 消息交换在目标和发起方这两端之间进行。
使用 SqlDependency 订阅查询通知是直接的:SqlDependency 对象将管理数据库中设置通知涉及到的复杂性。建立通知后,对象便会监视实现通知的基础数据库对象,当 SQL Server 创建查询通知时,将在应用程序中调用事件处理程序。
对于应用程序接收SQL Server Service Broker通知,只能获取到对应数据库表数据做了何种更新,而无法获取更新的数据,而我们却可以利用这个通知,来做缓存依赖,来达到缓存过期的目的。
使用查询通知的应用程序必须考虑到立即出现通知的情况。服务器上的数据更改时,通知消息将发送到相应的服务中介程序队列。应用程序需要注册才能接收其他通知。因此,如果多个应用程序快速更新某个数据集,应用程序在缓存刷新后,立即可以接收通知,检索数据,然后获取另一个更新通知。编写使用查询通知的应用程序时必须考虑到此情况。如果应用程序使用不断更新的数据,则可能更适合使用另一种数据缓存策略。
使用 SqlDependency 订阅查询通知必须向SQL Server Service Broker提供制定规则的查询语句,一般来讲,必须是简单的sql查询语句(不能用*,不能用top,不能用函数,包括聚合函数,不能用子查询,包括where后的子查询,不能用外连接,自连接,不能用临时表,不能用变量,不能用视图,不能跨库,表名之前必须加类似dbo数据库所有者这样的前缀),
例如:select * from table1,select column1 from table1,select count(*) from table1 都是错误的sql查询语句,select column1 from dbo.table1 则是正确的语句。
以下以一个实际的例子(sqlDep项目)来说明如何使用ServerBroker和SqlDependency类来做缓存依赖,充分利用服务器资源和提高应用程序性能,并且封装以提供给开发人员最大的便利性,我们需要按照如下步骤操作:
1. 启动Service Broker 服务支持
use master
alter database <dbname> SET ENABLE_BROKER
需要观察sys.databases的is_broker_enabled字段才知道是否已经启动,结果为1表示已开,0表示关闭
select is_broker_enabled from sys.databases where name = '<dbname>'
在本例中运行以下语句:
ALTER DATABASE AdventureWorks SET ENABLE_BROKER
以启用该功能,执行时必须关闭所有可能锁表的操作和作业。
2. 为访问数据库的帐号订阅查询通知
use <dbname>
GRANT CREATE PROCEDURE TO [UserName]
GRANT CREATE QUEUE TO [UserName]
GRANT CREATE SERVICE TO [UserName]
use <dbname>
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [UserName]
可以用下面语句查看赋给user的权限
exec sp_helprotect NULL, [UserName]
3. 在现有应用程序中增加更改通知以及缓存机制。
a) 在webconfig<configuration>节中添加<connectionStrings>节,并配置连接字符串。
b) 在webconfig<system.web>节中添加
<caching>
<cache percentagePhysicalMemoryUsedLimit="60" privateBytesPollTime="00:05:00" />
</caching> (此项配置全局缓存设置,可选)
c) 建立数据访问层,如何封装编写不限,只要具有返回数据的方法即可。
d) 嵌入或者重写DaBase.cs中的protected virtual DataTable GetDataTable方法,具体请参考sqlDep示例,该方法提供自动响应程序表发生的更改,自动设定缓存机制,封装此方法后,对于开发人员,只需要按照以往开发习惯提供任意sql语句编写程序获取数据。
e) 继承DaBase类或自己编写具有protected virtual DataTable GetDataTable方法的类,并调用该方法,参见DaDimCustomer.cs。
以下我们以sqlDep做测试,以验证可行性及其性能:
我们以SqlServer2005自带的AdventureWorksDW数据库中的DimCustomer表为例,该表有29列,各种数据类型都有,18484行,7984KB数据,平均每行0.43KB。
我们以每次查询20页,查询该表的所有列作为测试。由于缓存的是查询结果,所以内存变化可以根据每次查询的数据量为基准,20行大小大约是8.6KB,缓存默认设置是允许使用服务器内存的90%,
假设对应的数据库表不做更新操作,假设Web服务器有1G的内存可使用缓存,
则可以缓存12万份不重复结果(这里没有计算.net本身每个数据实体,每个缓存相关数据所占有的空间,相对于数据而言可以忽略不计),
缓存命中率大都集中在常用查询,例如商品列表第一页,某个商品分类第一页等,一旦有某个用户使用了查询,则其他用户可以不需要访问数据库即可得到所需数据。即使缓存如果超过了程序规定的最大数据,.net运行时也会自动随即清空缓存,这并不影响程序运行。
以下附上完整代码:
WebConfig文件:
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Code
<connectionStrings>
<add name="Conn" providerName="System.Data.SqlClient" connectionString="Data Source=localhost;Initial Catalog=AdventureWorksDW;User ID=sa;Password=sa"/>
</connectionStrings>
注意:对于使用本地系统帐户作为服务帐户的 SQL Server 实例,应用程序不会从其接收通知。
数据访问类:
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Code
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Configuration;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Data;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
using System.Data.SqlClient;
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
/**//// <summary>
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
/// SqlDbHelper 的摘要说明
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
/// </summary>
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
public sealed class SqlDbHelper
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
单态模式#region 单态模式
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static SqlDbHelper sqlDbHelper = new SqlDbHelper();
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
public static SqlDbHelper Instance()
![](http://www.cnblogs.com/Images/dot.gif)
{ return sqlDbHelper; }
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private SqlDbHelper()
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
this.connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Conn"].ConnectionString;
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
#endregion
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private string connectionString;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public string ConnectionString
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
get
![](http://www.cnblogs.com/Images/dot.gif)
{ return connectionString; }
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
set
![](http://www.cnblogs.com/Images/dot.gif)
{ connectionString = value; }
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public DataTable GetDataTable(string strSql)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
string connStr = this.connectionString;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
using (SqlConnection conn = new SqlConnection(connStr))
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlCommand comm = new SqlCommand(strSql, conn);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlDataAdapter da = new SqlDataAdapter(comm);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
DataTable dt = new DataTable();
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
da.Fill(dt);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return dt;
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public int ExecuteNonQuery(string strSql)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
string connStr = this.connectionString;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
using (SqlConnection conn = new SqlConnection(connStr))
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlCommand comm = new SqlCommand(strSql, conn);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (conn.State == ConnectionState.Closed) conn.Open();
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return comm.ExecuteNonQuery();
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public SqlDependency AddSqlDependency(string strSql, OnChangeEventHandler sqlDep_OnChange)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
string connStr = this.connectionString;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
using (SqlConnection conn = new SqlConnection(connStr))
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlCommand comm = new SqlCommand(strSql, conn);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 添加依赖
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlDependency sqlDep = new SqlDependency(comm);
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
sqlDep.OnChange += sqlDep_OnChange;
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (conn.State == ConnectionState.Closed) conn.Open();
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
comm.ExecuteNonQuery();
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return sqlDep;
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
/**//// <summary>
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
/// 对表增加依赖列,用于Sql依赖,或者用某个int列也可
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
/// </summary>
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
/// <param name="tableName">表名,如果不是dbo所有者,请提供包括所有者的完整表名</param>
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
/// <returns></returns>
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public int AddDependencyCloumn(string tableName)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
![](http://www.cnblogs.com/Images/dot.gif)
{
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
return this.ExecuteNonQuery(string.Format("declare @num int "
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
+ "set @num = (select count(*) from syscolumns where id=object_id('{0}') and name = 'dep') "
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
+ "if @num = 0 ALTER TABLE {0} ADD dep bit NOT NULL CONSTRAINT dep{0} DEFAULT 0 ", tableName));
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
}
![](http://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Code
using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.Caching;
/// <summary>
/// 要使用SqlServer2005 Service Broker,首先要在使用的数据库执行 ALTER DATABASE AdventureWorks SET ENABLE_BROKER 以启用该功能,执行时必须关闭所有可能锁表的操作和作业
/// 使用依赖的sql语句,不能用*,不能用top,不能用函数,包括聚合函数,不能用子查询,包括where后的子查询,
/// 不能用外连接,自连接,不能用临时表,不能用变量,不能用视图,不能垮库,表名之前必须加类似dbo数据库所有者这样的前缀
/// 依赖只针提供一次通知,所以重新发起某次查询则需要重新提供依赖sql语句
///
/// 优点:此应用比较适合访问次数大于更新次数的情况,访问次数比更新次数越多,速度提升越明显
/// 缺点:对服务器内存要求较高
/// </summary>
public abstract class DaBase
{
private SqlDbHelper sqlDbHelper;
//
private Cache pageCache;
public DaBase(Cache cache)
{
sqlDbHelper = SqlDbHelper.Instance();
this.pageCache = cache;
}
/// <summary>
/// 清除缓存
/// </summary>
/// <param name="cacheName">缓存名称</param>
protected virtual void ClearCache(string cacheName)
{
System.Collections.IDictionaryEnumerator cacheEnum = pageCache.GetEnumerator();
while (cacheEnum.MoveNext())
{
// 只清除与此业务相关的缓存,根据表名
if (cacheEnum.Key.ToString().ToLower().IndexOf(cacheName.ToLower()) > 0)
pageCache.Remove(cacheEnum.Key.ToString());
}
}
/// <summary>
/// 创建Service Borker通知(请确认Service Borker已开启),自动响应程序表发生的更改,自动设定缓存机制
/// </summary>
/// <param name="pageCache">System.Web.Caching.Cache对象</param>
/// <param name="selectSql">查询数据的sql语句</param>
/// <param name="dbOwner">数据库表所有者</param>
/// <param name="tableName">表名</param>
/// <param name="column">列名,随意某个小列(最好是bit,tinyint,varchar(1),int)</param>
/// <returns></returns>
protected virtual DataTable GetDataTable(string selectSql, string dbOwner, string tableName, string column)
{
// 用于Service Broker跟踪的表范围sql
string depSql = string.Format("select {0} from {1}.{2}", column, dbOwner, tableName);
DataTable dt = new DataTable();
if (pageCache[selectSql] != null)
dt = pageCache[selectSql] as DataTable;
else
{
// 触发行级依赖,如果该表的指定范围内的行被修改,则会收到SqlServer的通知,并且清空相应缓存
SqlDependency sqlDep = sqlDbHelper.AddSqlDependency(depSql,
delegate(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Invalid)
{
// sqlDbHelper.ExecuteNonQuery("ALTER DATABASE AdventureWorksDW SET ENABLE_BROKER");
// 写文件,数据库未开启Service Broker或者提供了无法通知的语句,例如没有写包括数据库所有者的表名。
}
this.ClearCache(tableName);
});
dt = sqlDbHelper.GetDataTable(selectSql);
pageCache[selectSql] = dt;
}
return dt;
}
}
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Code
using System;
using System.Data;
using System.Web;
using System.Web.Caching;
public class DimCustomer : DaBase
{
public DimCustomer(Cache pageCache) : base(pageCache) { }
#region 分页查询顾客信息
public DataTable SelectDimCustomer(int startIndex, int maxIndex)
{
// 用于查询的sql语句
string strSql = string.Format("with t as ( "
+ " select row_number() over(order by CustomerKey Desc) as rowNum, * "
+ " from DimCustomer where '1' = '1' "
+ ") select * from t where rowNum between {0} and {1} ", startIndex, maxIndex);
return base.GetDataTable(strSql, "dbo", "DimCustomer", "CustomerKey");
}
#endregion
}
测试页面:
![](http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
Code
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ServiceBrokerTest.aspx.cs" Inherits="ServiceBrokerTest" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txt1" Text="1" runat="server"></asp:TextBox>到<asp:TextBox ID="txt2" Text="20" runat="server"></asp:TextBox>行 <asp:Button ID="btn1" runat="server" Text="获取数据" OnClick="btn1_Click" />
<br />
<asp:GridView ID="gv1" runat="server" AutoGenerateColumns="true"></asp:GridView>
</div>
</form>
</body>
</html>
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class ServiceBrokerTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btn1_Click(object sender, EventArgs e)
{
DimCustomer da = new DimCustomer(this.Cache);
this.gv1.DataSource = da.SelectDimCustomer(Convert.ToInt32(this.txt1.Text), Convert.ToInt32(this.txt2.Text));
this.gv1.DataBind();
//SqlDbHelper sqlDbHelper = SqlDbHelper.Instance();
//DataTable dt = sqlDbHelper.GetDataTable("select top 10 * from DimCustomer");
//this.gv1.DataSource = dt;
//this.gv1.DataBind();
}
}
PS: 如果已经注册了通知服务,可以用以下语句查询消息队列。
select * from sys.dm_qn_subscriptions
总结:
特点:特别适合更新不频繁但是读取频繁的表,会大大提高应用程序性能。
优点:缓存越多,SQL服务器负担就越小,大大减少了IO读操作以及网络传输占用。
缺点:Web服务器会稍微增加缓存调度和内存增加的负担,并且在数据库相应表发生更改后服务器会清除该表相关的所有缓存。
何时使用主动式通知机制
查询通知是针对于并不经常改变的数据而设计的。最好把它应用于服务器端的应用程序(例如ASP.NET或remoting)而不是客户端应用程序(例如Windows表单应用程序)。记住,每一个通知请求都要在SQL Server中注册。如果你拥有大量的都有通知请求的客户端应用程序,那么这可能会导致你的服务器产生资源问题。鉴于此,微软推荐,对于客户端应用程序,你应该限制使用查询通知的最大并行用户数不多于十个。
对于大规模应用程序来说,查询通知可能是一种强有力的帮助,而不用简单地添加越来越多的服务器以满足要求。设想,有一家大型的为成千上百万用户提供在线软件更新服务的软件公司。不是使每一个用户的更新操作都触发服务器上的另一个查询来确定需要哪些组件,而是能够缓冲查询结果并且可以直接从该缓存中服务匹配的查询。
对于较小规模的情况而言,下拉式列表框是另一种典型的数据集;此时该数据集更新的次数一般不如请求的次数多。产品列表、州列表、国家列表、供应商、销售人,甚至更多不太需要频繁改变的信息正是使用上述通知机制的较好候选。
分享到:
上一篇:tree的xml文件解析
下一篇:解决dso framer打开多文档是的问题
查看评论
1楼 cjl_852006 2012-03-02 11:10发表 [回复]
![](http://avatar.csdn.net/E/7/3/3_cjl_852006.jpg)
有没有例子可以发给我学习学习?谢谢了
cjl-852010@hotmail.com
万分感谢!!!
发表评论
用 户 名:
winggyn
评论内容:
![](http://static.blog.csdn.net/images/ubb/code.gif)
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
![](http://static.blog.csdn.net/images/top.png)
个人资料
公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告QQ客服 微博客服 论坛反馈 联系邮箱:webmaster@csdn.net 服务热线:400-600-2320京 ICP 证 070598 号北京创新乐知信息技术有限公司 版权所有世纪乐知(北京)网络技术有限公司 提供技术支持江苏乐知网络技术有限公司 提供商务支持Copyright © 1999-2012, CSDN.NET, All Rights Reserved
![](http://csdnimg.cn/pubfooter/images/gongshang_logos.gif)
相关文章推荐
- SQL Server 2008中Service Broker基础应用(下)
- 转发:SQL Server 2008中Service Broker基础应用(上)
- SQL Server 2008中Service Broker基础应用(上)
- 转发:SQL Server 2008中Service Broker基础应用(下)
- SQL Server 2008中Service Broker基础应用(上)
- SQL Server 2008中Service Broker基础应用(下 )
- SQL Server 2008中Service Broker基础应用(上)
- SQL Server 2008中Service Broker基础应用(上)
- SQL Server 2008中Service Broker基础应用(下 )
- SQL Server 2008中Service Broker基础应用(下)
- SQL Server 2008空间数据应用系列一:空间信息基础
- SQL Server 2008空间数据应用系列一:空间信息基础
- SQL Server 2008空间数据应用系列一:空间信息基础
- SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础
- SQL Server 2008空间数据应用系列四:基础空间对象与函数应用
- SQL Server 2008空间数据应用系列二:空间索引(Spatial Index)基础
- SQL Server 2008空间数据应用系列四:基础空间对象与函数应用
- Analysis Services基础知识——深入SQL Server 2008
- [Sql Server 2008 基础] With Ties. Over()子句
- SQL Server 2008空间数据应用系列九:使用空间工具(Spatial Tools)导入ESRI格式地图数据