【问题标题】:Caching data from a DataContext从 DataContext 缓存数据
【发布时间】:2011-05-16 04:16:59
【问题描述】:

我们在 Web 应用程序中使用 Linq-to-SQL DataContext,它向应用程序提供只读数据并且永远不会更新(已设置 ObjectTrackingEnabled = false 以强制执行此操作。

由于数据永远不会改变(除了偶尔的配置更新),从 SQL Server 重新加载它并为每个 Web 请求使用新的 DataContext 似乎很浪费。

我们尝试在 Application 对象中缓存 DataContext 以供所有请求使用,但它会产生很多错误,我们的研究表明这是一个坏主意,DataContext 应该在同一个工作单元中处理,不是线程安全的,等等。

因此,由于 DataContext 是一种数据访问机制,而不是数据存储,因此我们需要考虑缓存我们从中获取的数据,而不是上下文本身。

希望对实体和集合本身执行此操作,这样代码就可以不知道它是在处理缓存数据还是“新”数据。

如何安全地做到这一点?

首先,我需要确保在处理 DataContext 之前已完全加载实体和集合。有没有办法方便地从数据库中强制加载所有内容?

其次,我很确定存储对实体和集合的引用是个坏主意,因为它要么

(a) 当 DataContext 超出范围时导致实体损坏或

(b) 防止 DataContext 超出范围

那么我应该克隆实体集并存储它们吗?如果是这样,怎么做?或者这是怎么回事?

【问题讨论】:

  • 似乎很浪费!= 性能瓶颈。 measuringprofiling 在哪里显示您的应用程序很慢?那是你应该集中精力的地方。这是直觉大多是错误的软件领域之一。
  • 嗨 mellamokb,感谢您的评论。这是真的,我们没有尝试测量。但是,当您知道每个 Web 请求只会每周更改一次时,通过连接/SQL 请求/磁盘访问重新加载静态数据怎么可能不浪费呢?您是说其他一些缓存本质上会在数据管道的下游发生吗?
  • 不,我是说当你不知道你的性能问题是什么时,你不应该随意添加缓存代码。你要缓存什么?每一个查询?具体表?缓存内存和数据库速度之间存在权衡,如果你用完所有内存,你的应用程序会更慢。你不能凭直觉回答所有这些问题。你怎么知道你添加的缓存层是否完成了这项工作?因为应用程序的某些部分感觉更快?这不是客观提高性能的方法。
  • 不是说这不是浪费。我不是说这样做不能提高性能。无论如何,如果你能做某事,那就去做吧!但是,如果你真的不知道你要解决的问题,而且你没有一个数字来衡量你的表现,那么你的目标是什么,你将如何到达那里?凭直觉和直觉来解决性能问题是完全错误的方法。
  • 感谢您的 cmets,mellamokb。我接受你的观点,我接受你的责骂。你仍然没有在这里添加任何建设性的东西。为了争论,我们假设我确实有一个定量分析方法。从 DataContext 缓存数据的最佳方法是什么?它不是一个大数据集(~5MB),所以缓存对内存的影响很小,特别是因为我将通过每个请求减轻服务器重复 DataContext 生命周期的负担。

标签: asp.net linq-to-sql


【解决方案1】:

这并不完全是您问题的答案,但我建议避免在网站端进行缓存。
我宁愿专注于优化数据库查询,以实现更快、更高效的数据检索。

缓存将:

  • 不可扩展
  • 需要额外的同步代码,我假设您的数据在 DB 中不是完全静态的?
  • 额外的代码容易出错
  • 会很快耗尽您的 Web 服务器的内存,接下来您可能要解决的问题是您的 Web 服务器上的内存问题
  • 当您需要对您的网站进行负载平衡时,不会很好地工作

[编辑]

如果我需要缓存 5MB 数据,我会使用 Cache 对象,可能是延迟加载。我会使用一组轻量级集合,例如ReadonlyCollection<T>Collectino<T>。我可能也会使用ReadonlyDictionary<TKey, TValue> 来快速搜索内存。我会使用 LINQ-to-Objects 来操作集合。

【讨论】:

  • 嗨亚历克斯,好点,但目前,我真的试图将整个结构化数据集读入内存以供所有线程共享。它不是一个大数据集(~5MB),所以缓存的内存影响很小,特别是因为我将通过每个请求减轻服务器重复的 DataContext 生命周期。真的只是想知道最好的方法。一旦我真正运行了缓存,我可以稍后计算出相对优点。
  • @Stephen - 如果这只是 5 MB,您为什么认为数据检索是个问题?
  • 我已经从 mellamokb 惹上了麻烦,因为我的直觉背后没有任何事实,你不要开始! :) 这是每个 Web 请求都重复需要的数据,而且几乎从不改变。我只是试图在我认为可以提高效率的地方进行防御性设置,而不是稍后处理明显的问题。
  • @Stephen - 我明白你的意思。我建议使用Cache 对象。
【解决方案2】:

您希望缓存从 DataContext 而不是 DataContext 对象本身检索到的数据。我通常将通常检索到的数据重构为可以实现静默缓存的方法,如下所示(可能需要添加线程安全逻辑):

public class MyBusinssLayer {

    private List<MyType> _myTypeCache = null;
    public static List<MyType> GetMyTypeList() {
        if (_myTypeCache == null) {
            _myTypeCache = // data retrieved from SQL server
        }

        return _myTypeCache
    }

}

这是可以使用的最简单的模式,将缓存一个网络请求。要缓存更长时间,请将内容存储在更长期的存储中,例如ApplicationCache。例如,要存储在Application 级别的数据,请使用这种模式。

public static List<MyType> GetMyTypeList() {
    if (Application["MyTypeCacheName"] = null) {
        Application["MyTypeCacheName"] = // data retrieved from SQL server
    }

    return (List<MyType>)Application["MyTypeCacheName"];
}

这适用于几乎从不改变的数据,例如在DropDownList 中可供选择的状态类型的静态集合。对于更易挥发的数据,您可以使用带有超时期限的Cache,应根据数据更改的频率进行选择。使用Cache 项目可以在必要时使用代码手动失效,或者使用像SqlCacheDependency 这样的依赖检查器。

希望这会有所帮助!

【讨论】:

  • 谢谢,这很好!我想知道我是否可以将 Linq-to-SQL 实体和 EntitySet 对象本身用作 MyType,换句话说,创建数据库的完整“离线”副本。这样我仍然可以获得关联的优势,并且代码可以不知道 DataContext 是否存在。但是 L2S 实体是否不会保留与其原始 DataContext 的某些连接,从而防止它超出范围?
  • 如果您加载数据并调用ToList() 以确保它是从数据库中提取的,那么只要您不遵循关联来检索必须延迟加载的数据(即,尚不可用),应该可以。事实上,您可以使用DataLoadOptions 指定应同时检索哪些相关关联,并且您可以通过一次调用数据库将整个数据库下载到内存中的集合中。
  • 这就是我的想法,但实体难道不以某种方式“属于”他们的 DataContext,即使 ObjectTrackingEnabled=false?我似乎记得必须在以前的项目中克隆实体,以便将它们从上下文中分离出来并在其他地方使用它们。
  • @Stephen:这取决于您如何使用它们。如果您只是从集合中读取它们以进行数据绑定,例如 GridView,那么它们是如何生成的并不重要。只要它是只读的,您就可以随意使用它们。如果您尝试跟踪相关对象的关联,并且您还没有加载所有必要的数据,那么它会变得具有挑战性。然后它会尝试连接到原始数据源并对丢失的数据进行延迟加载。
  • 它会找到原始数据源吗?彻底超时,但仍然因为实体引用而停留在内存中?这就是我所关心的情况。
猜你喜欢
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
  • 2014-08-07
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
相关资源
最近更新 更多