ASP.NET缓存预热策略

14

我有一个ASP.NET MVC 3/.NET Web应用程序,主要围绕“位置”(纽约、加利福尼亚等)的概念大量进行数据驱动。

无论如何,我们有一些非常繁忙的数据库查询,在完成后被缓存。

例如:

public ICollection<Location> FindXForX(string x)
{
   var result = _cache.Get(x.ToKey()) as Locaiton; // try cache

   if (result == null) {
      result = _repo.Get(x.ToKey()); // call db
      _cache.Add(x.ToKey(), result); // add to cache
   }

   return result;
}

我不想成为等待这个数据库调用的不幸首位用户。

数据库调用可能需要40-60秒钟,超过ASP.NET请求的默认超时时间。

我希望在我的应用程序启动或之后不久就为某些“热门”位置(例如纽约、加利福尼亚)预先运行这些调用。

我不想简单地在Global.asax(Application_Start)中执行此操作,因为应用程序启动时间太长了。(我计划预缓存大约15个位置,所以这是几分钟的工作。)

有没有什么办法可以异步触发这个逻辑?也许在旁边运行一个服务是更好的选择?

我能想到的唯一其他选择是拥有一个管理页面,该页面具有这些操作的按钮。因此,在应用程序启动后,管理员(例如我)可以触发这些查询。那将是最简单的解决方案。

有什么建议吗?


1
数据库调用可能需要40-60秒的时间 - 我假设你有适当的索引? - Mitch Wheat
@Mitch 是的,我们有索引。它的速度已经达到了极限。如果我们有一个查询“查找纽约所有位置”,它需要找到所有城市、社区和街道。可能会有500万条记录。为了节省其他500万条由于缓存而变得多余的记录,只需要一个不好的查询。相信我,这是值得的 - 这种疯狂的方法是有方法的。 :) - RPM1984
1
@RPM1984:顺便说一下,很棒的头像! - Brian
@Mitch Wheat - 我错了,我以为我们拥有的这个视图已经建立索引了。但实际上没有。:( 是我的错误...正在添加索引。 - RPM1984
added as answer below... - Mitch Wheat
显示剩余3条评论
8个回答

6

请查看针对IIS 7.5的“始终运行”应用程序设置。这个设置的基本作用是在现有应用程序池需要回收时,准备好一个新的应用程序池。当然,第一次可能需要40-60秒,但之后除非您物理重启机器,否则速度会很快。


6

一种快速简单的方法是在Application_Start中启动一个Task

但我发现将此功能包装到基础设施中会更好,这样你可以创建一个~/Admin/CacheInfo页面来监视缓存加载过程中的进度、状态和异常情况。


3

在开始缓存预热之前,建议您首先查看查询语句执行的逻辑读取次数,确保其已达到最快速度。


3

看起来你应该把结果存储到一个单独的表中,并有一个定期重新填充该表的计划任务。

如果一个预计算的表不够,因为它最终会产生太多需要搜索的数据,那么你可以使用多个表。


2

有一个解决方案是在你的Application_Start方法中启动一个工作线程,在后台进行预热。如果你做得好,你的应用程序启动时间不会变长,因为该线程将异步执行。


他在问题中说他不想这样做。 - Shan Plourde
3
@Shan Plourde - 我说过我不想在application_start方法中等待,但是使用工作线程就可以了。目前我倾向于我的原始提议(创建一个管理员页面,这样我可以更好地监控缓存、手动使某些项目失效、预热等)。 - RPM1984
嗯,你的说法“我不想简单地在Global.asax(Application_Start)中执行此操作,因为应用程序启动需要太长时间。(我计划预缓存大约15个位置,所以这是几分钟的工作)。”似乎与你的评论有些不同...所以是的,Application_Start非常适合。 - Shan Plourde

2

一种选择是使用网站健康监控服务。它可以用于检查网站健康状况,如果定期调度,则可以调用常见的URL。


1

Application_Start中使用Task进行加载是正确的方法,正如Scott所提到的。

只要小心 - 如果您的站点重新启动并且有10个人尝试查看加利福尼亚州,您不希望同时出现10个实例的_repo.Get(x.ToKey()); // call db尝试加载相同的数据。

在应用程序状态中存储一个布尔值"IsPreloading"可能是一个好主意。在预加载函数开始时将其设置为true,在结束时将其设置为false。如果设置了该值,请确保不要在FindXForX中加载任何15个预加载位置之一。


1
双重检查锁定可用于您建议的内容:http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Microsoft_.NET_.28Visual_Basic.2C_C.23.29 - ciscoheat

1

如果您的应用程序是负载均衡的,建议查看auto-starting


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接