【问题标题】:Optimise in-memory caching of hierarchical structure from SQL Server 2008 R2从 SQL Server 2008 R2 优化分层结构的内存缓存
【发布时间】:2011-04-18 17:08:51
【问题描述】:

我正在 Sql Server 中为 C# Asp.Net MVC3 网站实现内容数据库。

表结构基本上是一个邻接列表,但我的文件和文件夹被划分为FileSystems,以便我可以隔离各个用户/帐户的内容:

FileSystem:
  [ID]

Folder:
  [ID]
  [FileSystemID]
  [ParentFolderID] (null)
  [Name]

File:
  [ID]
  [FileSystemID]
  [ParentFolderID]
  [Name]
  [Content]

考虑到这里的低效标准化,这是非常基本的东西。

如果适用,我还有[Created][Modified] 的列。

这些文件将用于在 Asp.Net MVC 3 站点中执行页面的动态品牌化,其中每个请求都将寻找基于原始 Asp.Net 的更具体的品牌化版本的 css/图像文件MVC 内容网址 - 例如"~/Content/Site.css" 可能会变成 "~/[dynamic content root]/[accountid]/[theme]/Site.css"

这个基本机制已经全部起作用了;我主要关心的是缓存和版本控制。

显然,与其一直在数据库中搜索相同的文件和文件夹,不如构建内容文件系统的内存缓存以加快内容查找和交付速度。但是,我需要一种有效的方法来确保网络场中的所有网络服务器都检测到帐户虚拟文件系统中的更改(任何文件或文件夹更改/删除/创建),以确保主题更改立即反映在所有服务器上,例如下一个请求。

由于分层查询可能很昂贵,因此我不考虑每次对文件系统中的所有创建/上次修改日期运行健全性检查。然而,我考虑的是整个文件系统上的级联版本号。

因此,文件系统中的任何 更改都会导致文件系统本身的版本号增加,并且可能会导致更改项之后的所有父文件夹增加。因此,阅读器可以简单地附加到整个文件系统或其中的特定部分,并每次运行廉价查询来检查当前版本与缓存版本。

这确实有减慢更新速度的缺点,但我希望文件系统不会经常更改,而会非常频繁地读取它。我对这种方法唯一关心的是更新的并发性,以及如何管理它。

这是一个好方法吗?有什么更好的我可以考虑的吗?

欢迎任何想法!

【问题讨论】:

    标签: c# sql asp.net-mvc


    【解决方案1】:

    由于您使用的是 SQL Server,因此我建议您使用 SqlCacheDependency 对象或 SqlDependency 对象作为 SQL Server 中Query Notifications 服务的一部分。

    我已经在各种项目中成功地使用了它,导致通知的负担在数据库上,而不是我自己编写的一些轮询机制。这是我如何使用它来缓存角色信息的示例:

    public CacheDependency GetRoleActionCacheDependency()
        {
            using (var connection = new SqlConnection(Database.Database.Connection.ConnectionString))
            {
                connection.Open();
                using (SqlCommand sc = new SqlCommand("select roleid, actionid from dbo.RoleAction", connection))
                {
                    var dependency = new SqlCacheDependency(sc);
                    sc.ExecuteNonQuery();
                    connection.Close();
                    return dependency;
                }
            }
        }
    

    每当角色操作表中的任何内容发生更改时,此缓存依赖项都会使缓存无效。我可以通过在查询中添加参数来获取行级通知。

    我是这样称呼这段代码的。您可以将实际的依赖对象存储在缓存中,但在我的特定情况下,对象实例本身存在于应用程序中(静态),因此我不需要缓存它本身,我只需要使其无效。我在这里通过将其设置为 null 来执行此操作(getter 管理重新填充它)。

    CacheDependency rolePathAccessCacheDependency = GetRoleActionRepository().GetRoleActionCacheDependency();
        HttpContext.Current.Cache.Add("anything will do", new object(), rolePathAccessCacheDependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Normal,
                        (key, value, reason) =>
                        {
                            _rolePathAccess = null; 
                        });
    

    还要在 global.asax 应用程序启动中完成示例:

     SqlDependency.Start(ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString);
    

    及应用端:

    SqlDependency.Stop(ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString);
    

    我忘记的另一件事是它依赖于启用 SQL Server 中的代理服务。这是一种启用它的方法,但请注意,第一条语句神奇地赋予您对数据库的独占访问权限并回滚其他所有内容,因此只有在您知道自己在做什么的情况下才能在生产中使用它。如果您已经拥有独占访问权限,那么第二个语句就是您真正需要的。

    ALTER DATABASE MYDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    ALTER DATABASE MYDatabase SET ENABLE_BROKER
    ALTER DATABASE MYDatabase SET MULTI_USER
    GO        
    

    【讨论】:

    • 啊,是的,我确实想知道是否可以使用 this 作为答案。我以前看过它并对 db 修改犹豫不决,尽管这是一个全新的数据库,所以我可以摆脱它。我会玩一会,看看我的表现如何,谢谢。
    • @Andras Zoltan 我已经成功使用了这些功能。我在启用代理服务方面也从来没有遇到过麻烦,当然我也不必亲吻任何 DBA,因为我在一家小公司。
    • 我需要多考虑一下:我正在使用存储库模式;所以看看我如何将这个非常具体的功能融入其中将会很有趣——存储库接口将不得不公开缓存依赖端点;但这是特定于 ASP.net 的。啊,多层设计的诅咒。
    • @Andras Zoltan - 您可以将 SqlDependency 类包装在您自己的类中,该类实现您自己的接口,并从存储库返回您的接口。 SqlDependency 类不是 ASP.NET 特定的,因为它位于 System.Data.SqlClient 命名空间中。然后,您可以实现自己的 CacheDependency 类,该类在内部使用您的新接口。您将获得相同的功能,并且仍然能够将您的存储库与其他应用程序或其他存储库一起使用。我在 Oracle 上做了很多事情。
    【解决方案2】:

    只是我的一些随机想法:

    您真的有性能问题吗?您能否将它们追溯到这个特定问题?您是否需要快速修复,或者您是否正在深入研究更广泛的问题。您寻找的吞吐量是多少,一些数字?

    浏览器和您的服务器之间有几层,如果您允许,“网络”也可以在浏览器代理等中进行缓存。然后是 IIS 本身旨在处理缓存。 mvc 与那些(相对)静态 url 一起工作的方式,这两个“开箱即用”。

    下一层将是您的 MVC 对象模型,您可以将整个树保存到内存中吗?这样您就不必进行进程外调用。这样可以节省不少周期,更不用说去磁盘了。如果我理解正确,您仍然会共享很多文件,因此您可以共享这些文件的实例(享元模式)。在需要时延迟加载它们,并在需要时释放它们。内存比你的时间便宜。

    rgds GJ

    【讨论】:

    • 嗯,还没有性能问题,但它还没有上线!可能的过早优化,是的,但是经验和流量水平的估计只是说尽可能少地访问数据库是最好的路径。我也将利用浏览器缓存;但只是想确保,当我实现服务器缓存时,我的数据库结构适合它。 Quesi 的回答很好,因为我不需要做太多的事情来确保这一点,但你确实在这里提出了有效的观点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    相关资源
    最近更新 更多