SQL缓存策略
我正在开发一个交互式联系人搜索页面(当您键入或选择条件时,通过ajax返回联系人)。我希望这一页是非常敏感的SQL缓存策略,sql,sql-server,sql-server-2005,optimization,caching,Sql,Sql Server,Sql Server 2005,Optimization,Caching,我正在开发一个交互式联系人搜索页面(当您键入或选择条件时,通过ajax返回联系人)。我希望这一页是非常敏感的 有一套复杂的规则来确定给定联系人可以看到哪些联系人记录;这些规则汇总到用户定义的函数中,DirectoryContactsByContact(@ContactID)。我已经对该函数进行了大量优化,但它仍然有点昂贵(执行时间为1-2秒),因此为了提高性能,我考虑了以下方法: 加载页面时,将此用户的DirectoryContactsByContact缓存为SQL表,例如,cache\u D
有一套复杂的规则来确定给定联系人可以看到哪些联系人记录;这些规则汇总到用户定义的函数中,
DirectoryContactsByContact(@ContactID)
。我已经对该函数进行了大量优化,但它仍然有点昂贵(执行时间为1-2秒),因此为了提高性能,我考虑了以下方法:
- 加载页面时,将此用户的DirectoryContactsByContact缓存为SQL表,例如,
cache\u DirectoryContactsByContact\u 1
- 对缓存的表执行搜索(每次检查以确保它存在)
- 过一会儿(比如30分钟)杀死缓存
SQL Server中是否有任何机制可以简化此过程?有其他方法的建议吗?在页面加载时,将函数的结果插入到一个永久表中,比如SearchResults。此表将包含如下字段:
- 搜索联系人ID
- 目录联系人ID
- 创建日期
这是否意味着搜索的数据是“很多”,或者搜索结果是“很多”?DirectoryContactsByContact(@ContactID)的输出有多大?我的假设是,这是一个小的结果集,小到足以在ASP端使用。如果这是真的,那么您应该在ASP中缓存特定@ContactID的搜索结果,并对相同的重复@ContactID恢复该缓存结果,直到它从缓存中过期,然后重新创建它 我不太喜欢将结果缓存为SQL中的表。这种方法将读变为写,从而进一步降低第一次命中的速度。它提供陈旧的数据,需要清理。但最重要的是,根据我的经验,它总是避免了由于数据模型模式设计不当而导致的查询不足的实际问题
您对
DirectoryContactsByContact(@ContactID)
响应时间不能进一步缩短有多大信心?瓶颈在哪里?你是怎么测量的?您是否考虑过可以进行哪些模式更改以更快地获得此结果?我最终创建了一个基本的通用框架,用于将SQL函数或视图的结果缓存到表中
Public Sub CreateCacheTable(ByVal SourceView As String, ByVal FieldList As String)
Dim CacheTable As String = GetCacheTableName(SourceView)
If Not TableExists(CacheTable) Then
Dim Sql As String = " Select ~FieldList~ Into ~CacheTable~ From ~SourceView~ ". _
Replace("~CacheTable~", CacheTable). _
Replace("~FieldList~", FieldList). _
Replace("~SourceView~", SourceView)
ExecuteNonQuery(cs, CommandType.Text, Sql)
End If
End Sub
Public Function GetCacheTableName(ByVal SourceView As String)
Dim Result As String = "_c_~SourceView~". _
Replace("~SourceView~", SourceView). _
Replace(".", "_"). _
Replace(",", "_"). _
Replace("[", ""). _
Replace("]", ""). _
Replace("(", ""). _
Replace(")", "")
Return Result
End Function
Public Sub CleanupCacheTables()
ExecuteNonQuery(cs, CommandType.StoredProcedure, "CleanupCacheTables")
End Sub
加载页面时,我会执行以下操作:
CleanupCacheTables()
CreateCacheTable(SourceView, FieldList)
例如,如果SourceView为
DirectoryContactsByContact(123)
,则会创建一个名为\u c\u DirectoryContactsByContact\u 123
的表
以下是清理缓存表的SQL:
Create Procedure CleanupCacheTables as
/* Finds all tables starting with _c_ that were created more than 30 minutes ago and drops them */
Declare @TableName nvarchar(255)
Declare CacheTableCursor Cursor for
Select
TableName=name
From SYS.OBJECTS
Where Type_Desc = 'USER_TABLE'
And Left(name,3)= '_c_'
And DateDiff(minute, create_date, GetDate())>30
Open CacheTableCursor
Fetch Next from CacheTableCursor into @TableName
While @@FETCH_STATUS = 0 Begin
Exec ('Drop Table ' + @TableName)
Fetch Next from CacheTableCursor into @TableName
End -- While
Close CacheTableCursor
Deallocate CacheTableCursor
Go
这是粗糙的:没有失效,而且可能无法扩展到大量并发用户和/或非常大的数据集。然而,在我的例子中,当用户键入或选择搜索条件时,它会产生近乎即时的结果,开销很小 FWIW我不想将数据缓存在.NET的内存中,因为(a)有很多数据,(b)搜索涉及全文索引和连接以及SQL擅长的其他内容。因此,我肯定要将其缓存为SQL表。
DirectoryContactsByContact(@ContactID)
可能包含超过20万行。但是(b)是一个更大的问题-一旦它被缩小到这个联系人可以看到的范围,仍然需要进行一些复杂的过滤,SQL更适合于这些过滤。DirectoryContactsByContact(@ContactID)强制执行的业务规则太复杂,无法在这个空间中讨论;只要说有很多事情在进行就够了。无论如何,查询缓存表和从冷启动(15毫秒到600毫秒)查询函数之间有大约40倍的差异,我怀疑通过调整查询是否可以挤出这种性能。我明白了。我在你原来的帖子里读到了这句话,但我不得不要求你确认一下。你最好的选择是西尔维亚建议的。这不是个坏主意。但是,如果我要麻烦地运行自己的缓存基础设施,那么(在我的情况下)缓存所有被查询的数据而不仅仅是ContactID是有意义的,这样我就不必在用户键入时进行任何加入或任何工作。