数据库缓存依赖
数据库缓存依赖,在数据库中的数据更改的时候,它能自动的使缓存中的数据失效。这个特征仅在SQL 2005以及以后的版本中适用。
为了理解SQL 缓存依赖的工作原理,先了解一些过去常用的有缺陷的解决方案。
一个常用的技术是使用标记文件。使用这种技术,你在缓存里增加一个数据对象,然后建立起一个文件依赖。然而,这个文件是空文件,你只是用来做标记。
当用户调用存储过程改变表里的数据,存储过程会删除或者修改标记文件。ASP.NET会立即侦测到这个改变,然后去除符合的缓存项。这个丑陋的工作环境没有伸缩性,并且多个存储过程一起调用的时候,可能会有并发问题。这也迫使你存储过程代码混乱,因为每个存储过程要有相似的文件修改逻辑。让数据库系统和文件系统交互是一个坏主意,增加复杂度减少安全性。
另一个常用的方法是使用自定义的http handler。在这种情况下,代替交互文件,这些存储过程调用http handler并且船体query string来指示数据被改动。http handler能够使用Cache.Remove来去除数据。
使用这种方法的问题在于,它对存储过程的扩展有相当大的复杂性,而且,http handler请求必须是同步的,这就导致明显延迟。更糟的是,延迟在每次存储过程执行时出现。
因此,我们需要的是一个可以异步通知的方法,并且是可伸缩可靠的。也就是说,数据库服务器应该通知ASP.NET而不需要停止当前链接。重要的是,它应该松耦合的建立缓存依赖,存储过程不需要知道缓存这回事情。数据库服务器应该监控数据改变,通过各种方法,包括脚本,sql命令,或者批处理。即使改变不是直接由期望的存储过程引发,这个改变也要被通知到ASP.NET.最终,这个通知方法要能支持web farm。
微软把ASP.net,SQL Server, ADO.NET,IIS开发组中的架构师集中起来,一起实现一个解决方案。他们提出2种不同的架构,一种是关于SQL Server 2000 。另一种是SQL Server 2000后续版本。比如SQL Server 2005.他们都用到了SqlCacheDependency 类,该类从CacheDependency 继承。
使用SQL缓存依赖,相对于基于时间的过期策略来说还是非常复杂。如果对实时性要求不是很高,不需要使用它。
缓存通知的工作原理
SQL Server 2005将通知架构和消息系统内建在数据库中,叫做Service Broker。Service Broker管理队列,这个队列和表,存储过程或者试图有相同的地位。
使用Service Broker,你能够从特定的数据库事件中获得通知,最直接的方法是使用CREATE EVENT NOTIFICATION 命令来指示你要监控的事件。但是,.NET提供了一个更高级的模型,和ADO.net整合在一起。使用这个模型,你可以很简单的注册一个查询命令,.NET也会自动指导SQL服务器发送通知。ASP.NET在这个基础上提供一个更高级别的模型,允许你在一个查询无效的时候自动的使cache项无效。
SQL Server通知机制工作起来和索引视图类似。每次执行一个操作,SQL Server决定该操作是否影响了注册的命令,如果是,会发送一个通知消息,并且停止通知进程。
如下图所示:
启用通知
仅有的配置就是确定你设置了数据库的ENABLE_BROKER标志
Use Northwind
ALTER DATABASE Northwind SET ENABLE_BROKER
通知由Select和存储过程一起工作。然而,使用select语法时有一些限制,你必须遵守:
必须使用表的全名,[Owner].table
不能使用聚集函数,比如count(),MAX()
不能用通配符*来选择所有列。只能写成每个列名。
这些是最重要的规则,联机帮助上还有更多的警告和说明。
创建缓存依赖
当创建缓存依赖时,SQL server需要知道你用来获取数据的数据库命令。如果你使用编程的缓存,你必须创建SqlCacheDependency对象,使用构造器来接受一个SqlCommand对象。
// Create the ADO.NET objects.
string connectionString = WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
SqlConnection con = new SqlConnection(connectionString);
string query ="SELECT EmployeeID, FirstName, LastName, City FROM dbo.Employees";
SqlCommand cmd = new SqlCommand(query, con);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
// Fill the DataSet.
DataSet ds = new DataSet();
adapter.Fill(ds, "Employees");
// Create the dependency.
SqlCacheDependency empDependency = new SqlCacheDependency(cmd);
// Add a cache item that will be invalidated if one of its records changes
// (or a new record is added in the same range).
Cache.Insert("Employees", ds, empDependency);
你也需要调用静态方法SqlDependency.Start() 来初始化监听Web服务器的服务。这只需要对每个数据库连接执行一次,通常调用的地方是在global.asax文件中的Application_Start() 方法中。
SqlDependency.Start(connectionString);
这个方法打开一个新的,非池化的数据库连接。ASP.NET使用这个连接检查通知队列。初次调用Start(), 生成一个有着独一无二名字的新的队列会自动,新的通知服务也随即生成,然触发一个后监听开始。当通知收到,Web Services把通知放入队列,SqlDependency.OnChange事件,是缓存项无效。
即使你有在几个不同的表上有依赖,他们使用的还是相同的队列。这意味着你只需要调用SqlDependency.Start()。如果你不小心调用SqlDependency.Start()方法多次,不会有任何事发生。最后,你可以使用下面的代码分离监听。
SqlDependency.Stop(connectionString);
通常,在Application_End() 方法中分离监听,释放资源。