Оптимизация кэширования в памяти иерархической структуры из SQL Server 2008 R2 - PullRequest
3 голосов
/ 18 апреля 2011

Я реализую базу данных контента для веб-сайта C # Asp.Net MVC3 в Sql Server.

Структура таблицы в основном представляет собой список смежности, но мои файлы и папки разбиты на FileSystem s, чтобы я мог изолировать содержимое отдельных пользователей / учетных записей:

FileSystem:
  [ID]

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

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

Если учесть здесь неэффективную нормализацию, это довольно простые вещи.

У меня также есть столбцы для [Created] и [Modified], где это применимо.

Эти файлы будут использоваться для динамического брендинга страниц на сайте Asp.Net MVC 3, где каждый запрос будет искать более специфические фирменные версии файлов css / image на основе оригинальной Asp.Net URL контента MVC - например "~/Content/Site.css" может стать "~/[dynamic content root]/[accountid]/[theme]/Site.css".

Этот базовый механизм все уже работает; моя главная задача - кэширование и управление версиями.

Очевидно, что вместо того, чтобы постоянно проверять базу данных на наличие одних и тех же файлов и папок, имеет смысл создать кэш-память в файловой системе (ах) контента для ускорения поиска и доставки контента. Однако мне нужен эффективный способ гарантировать, что все веб-серверы в веб-ферме обнаружат изменение в виртуальной файловой системе учетной записи (любой файл или папка были изменены / удалены / созданы), чтобы изменение темы отражалось мгновенно на всех серверах, для следующий запрос.

Поскольку иерархические запросы являются потенциально дорогостоящими, я отказался от выполнения проверки работоспособности для всех созданных / последних измененных дат в файловой системе каждый раз. Однако я рассмотрел каскадный номер версии во всей файловой системе.

Таким образом, любое изменение в файловой системе приводит к увеличению номера версии самой файловой системы и, возможно, всех родительских папок по сравнению с измененным элементом. Таким образом, читатель может просто подключиться ко всей файловой системе или к определенной ее части и каждый раз выполнять недорогой запрос, чтобы сравнить текущую версию с версией кеша.

У этого недостатка есть замедление обновлений, но я ожидаю, что файловая система будет меняться нечасто, хотя она будет читаться очень часто. Мое единственное беспокойство в связи с этим подходом - это параллелизм в обновлениях и как им управлять.

Это был бы хороший подход? Есть что-то лучшее, что я мог бы рассмотреть?

Любые мысли приветствуются!

Ответы [ 2 ]

3 голосов
/ 18 апреля 2011

Поскольку вы используете SQL Server, я рекомендую использовать что-то вроде объекта SqlCacheDependency или объекта SqlDependency в составе служб Уведомления о запросах в SQL Server.

Я успешно использовал это в различных проектах, в результате чего бремя уведомлений было в базе данных, а не какой-то механизм опроса, который я пишу сам.Вот пример того, как я использую его для кэширования информации о ролях:

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 (геттер управляет его повторным заполнением).

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        
0 голосов
/ 19 апреля 2011

Всего несколько случайных мыслей от меня:

У вас действительно есть проблемы с производительностью, и не могли бы вы отследить их до этой конкретной проблемы? Вам нужно быстрое решение или вы углубляетесь в более широкий вопрос. Какую пропускную способность вы ищете, какие цифры?

Существует несколько слоев между браузером и вашими серверами, «веб» может также выполнять кеширование в прокси браузеров и т. Д., Если вы позволите. Тогда есть сам IIS, который предназначен для обработки кэширования. То, как mvc работает с этими (относительно) статическими URL, эти два «из коробки».

Следующим слоем будет ваша объектная модель MVC. Можете ли вы сохранить все дерево в памяти? Таким образом, вам не придется делать вызовы из процесса. Это экономит довольно много циклов, не говоря уже о переходе на диск. Если я правильно понимаю, вы по-прежнему будете делиться большим количеством файлов, чтобы вы могли поделиться экземпляром этих файлов (шаблон flyweight). Ленивый загружать их при необходимости и иметь механизм, чтобы освободить, когда вам это нужно. Память дешевая по сравнению с твоим временем.

ргдс ГДж

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...