【问题标题】:Does any asp.net data cache support background population of cache entries?是否有任何 asp.net 数据缓存支持缓存条目的后台填充?
【发布时间】:2011-03-23 21:41:49
【问题描述】:

我们有一个数据驱动的 ASP.NET 网站,该网站使用标准数据缓存模式编写(此处改编自 MSDN):

public DataTable GetData()
{
   string key = "DataTable";
   object item = Cache[key] as DataTable;
   if((item == null)
   {
      item = GetDataFromSQL();
      Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300), TimeSpan.Zero;
   }
   return (DataTable)item;
}

这样做的问题是对 GetDataFromSQL() 的调用很昂贵,并且该站点的使用率很高。因此,每五分钟,当缓存下降时,网站就会变得非常“粘滞”,而大量请求正在等待检索新数据。

我们真正想要的是让旧数据保持最新状态,而新数据会在后台定期重新加载。 (因此有人可能会看到 6 分钟前的数据这一事实并不是什么大问题 - 数据不是 时间敏感的)。这是我可以自己编写的东西,但是了解是否有其他缓存引擎(我知道 Velocity、memcache 之类的名称)是否支持这种情况会很有用。还是我错过了标准 ASP.NET 数据缓存的一些明显技巧?

【问题讨论】:

  • 分析您的 GetDataFromSQL 调用并找出其中的哪些部分很慢可能是值得的。它很可能遵循顶叶原则(即 80% 的减速是由 20% 的代码造成的)。然后,您可以将精力集中在导致大部分速度下降的那 10% 或 20% 的代码上。
  • 分析将使您更好地了解数据使用模式。这应该允许您只缓存那些变化不大的东西。否则,您可能会花费大量时间在后台填充很少使用的缓存条目,这实际上可能会降低性能。
  • 感谢您的这些想法。但是:a)有各种背景原因,这意味着调用目前仍然很昂贵,并且 b)我认为无论调用费用如何,后台加载模式在这些情况下实际上是架构上最好的。
  • @Robert:帕累托原则? en.wikipedia.org/wiki/Pareto_principle

标签: asp.net caching


【解决方案1】:

您应该能够使用 CacheItemUpdateCallback 委托,它是第 6 个参数,它是使用 ASP.NET 缓存的 Insert 的第 4 个重载:

Cache.Insert(key, value, dependancy, absoluteExpiration,
    slidingExpiration, onUpdateCallback);

以下应该有效:

Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300),
    Cache.NoSlidingExpiration, itemUpdateCallback);

private void itemUpdateCallback(string key, CacheItemUpdateReason reason,
    out object value, out CacheDependency dependency, out DateTime expiriation,
    out TimeSpan slidingExpiration)
{
    // do your SQL call here and store it in 'value'
    expiriation = DateTime.Now.AddSeconds(300);
    value = FunctionToGetYourData();
}

来自MSDN

当一个对象在缓存中过期时, ASP.NET 调用 CacheItemUpdateCallback 方法与 缓存项的键和 您可能想要更新的原因 物品。这个的其余参数 方法是输出参数。你供应 新的缓存项和可选 到期和依赖值 刷新缓存项时使用。

更新回调不会被调用,如果 缓存的项目被显式删除 通过调用 Remove()。

如果您希望缓存的项目是 从缓存中删除,您必须 在昂贵的对象中返回 null 范围。否则,您返回一个 引用新的缓存数据 使用昂贵的对象参数。 如果您未指定到期时间或 依赖值,该项目将是 仅当从缓存中删除 需要内存。

如果回调方法抛出一个 例外,ASP.NET 抑制 异常并删除缓存 价值。

我尚未对此进行测试,因此您可能需要对其进行一些修改,但它应该可以让您大致了解您要完成的工作。

【讨论】:

  • +1。绝对解决了 OP 对“旧数据保持最新,而新数据在后台定期重新加载”的要求。很好的解决方案!
  • 我一直认为这个调用是在对象过期之后进行的……谁能想到阅读文档会派上用场?干得好。
  • 一个警告。如果您的 FunctionToGetYourData() 长时间运行,您将在重新填充期间停止缓存。这意味着代码可以取出未过期的条目,但在回调返回之前不能缓存新条目(可能是为了响应其他页面请求)。这看起来真的很糟糕。
【解决方案2】:

首先,将您实际需要的日期放在精益类(也称为 POCO)中,而不是那个 DataTable hog。

其次,使用缓存和哈希 - 这样当您的时间依赖到期时,您可以生成一个异步委托来获取新数据,但您的旧数据在单独的哈希表中仍然是安全的(不是字典 - 它对于多阅读器不安全单写线程)。

根据数据类型和重构 SQL 端的时间/预算,您可能只获取 LastWrite 比您的更新窗口更年轻的内容。您将需要两步更新(必须将数据从 hash-kept opject 复制到新对象中 - hash 中的内容对于任何用途都是严格只读的,否则会崩溃)。

哦,而且 SqlCacheDependency 因不可靠而臭名昭著,并且可能使您的系统陷入疯狂的更新。

【讨论】:

  • 我确信这是善意的,但我认为其中任何一个都不能真正解决我的问题。我真的不需要关于如何围绕问题进行编码的指针(为了激发问题,我的描述可能过于天真了)。我真的很感兴趣,是否有任何数据缓存替代方案可以体现我所描述的模式,在我看来,这似乎是一种显而易见的要求。
【解决方案3】:

我可以看到使用AppFabric(以前称为Velocity 的缓存)有一个潜在解决方案,它允许您lock 缓存项目,以便可以更新它。当项目被锁定时,普通(非锁定)Get 请求仍然正常工作并返回缓存的当前项目副本。

这样做还可以让您将 GetDataFromSQL 方法分离到不同的进程,例如每五分钟运行一次的 Windows 服务,这应该可以减轻您的“粘性”网站。


或者……

与其一次只将数据缓存五分钟,不如在将数据放入缓存时使用SqlCacheDependency 对象,这样只有在数据实际更改时才会刷新它。这样,您可以将数据缓存更长时间,从而获得更好的性能,并且您将始终显示最新数据。

(顺便说一句,当您将对象放入缓存时,让您的意图更清晰的重要提示 - 缓存有一个可用的 NoSlidingExpiration(和一个 NoAbsoluteExpiration)常量,它比您的 Timespan.Zero 更具可读性)

【讨论】:

  • 谢谢你,但我不认为它有帮助。锁定对象以进行线程安全更新并不是什么大问题。而且我不想使用 SqlCacheDependency 因为我不想打开 Service Broker(加上它的使用不适合特定的应用程序)。
猜你喜欢
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-18
相关资源
最近更新 更多