Microsoft Azure быстро становится популярным облаком для .NET-приложений. Кроме богатого набора облачной функциональности, Azure обеспечивает полную интеграцию с Microsoft .NET Framework. Это также хороший выбор для приложений на Java, PHP, Ruby и Python. Многие приложения, перемещаемые в Azure, генерируют интенсивный трафик и требуют поддержки высокой масштабируемости. Распределенный кеш в памяти (in-memory distributed cache) может быть важным компонентом масштабируемой среды.
В этой статье рассматривается распределенное кеширование в целом и то, чем оно может быть полезно.
Средства, описываемые здесь, относятся к универсальному распределенному кешу в памяти, а не конкретно к Azure Cache или NCache for Azure. .NET-приложениям, развертываемым в Azure, распределенный кеш в памяти дает три основных преимущества.
- Производительность и масштабируемость приложений.
- Кеширование состояния сеанса, состояния представления (view state) и вывода страниц ASP.NET.
- Совместное использование данных периода выполнения через события.
Производительность и масштабируемость приложений
Azure упрощает масштабирование инфраструктуры приложения. Например, вы можете легко добавить больше веб- и рабочих ролей, или виртуальных машин (virtual machines, VM), предвидя более высокую транзакционную нагрузку. Несмотря на такую гибкость, хранилище данных может оказаться узким местом и воспрепятствовать масштабированию вашего приложения.
В отличие от реляционной базы данных распределенный кеш в памяти масштабируется линейно.
Здесь-то и может помочь распределенный кеш в памяти. Он позволяет кешировать столько данных, сколько вам нужно. Он способен сократить количество дорогостоящих операций чтения базы данных на 90%. Это также уменьшает транзакционное давление на базу данных. Она сможет работать быстрее и справляться с большей транзакционной нагрузкой.
В отличие от реляционной базы данных распределенный кеш в памяти масштабируется линейно. Как правило, он не становится узким местом для масштабирования, даже если 90% трафика чтения будут адресоваться кешу, а не базе данных. Все данные в кеше распределены по множеству серверов кешей. Вы сможете легко добавлять больше серверов кешей по мере роста вашей транзакционной нагрузки. На рис. 1 показано, как направлять приложения к кешу.
Рис. 1. Применение распределенного кеша в памяти в .NET-приложениях
// Проверяем кеш перед обращением к базе данных
Customer Load(string customerId)
{
// Ключ (key) будет чем-то вроде Customer:PK:1000
string key = "Customers:CustomerID:" + customerId;
Customer cust = (Customer)Cache[key];
if (cust == null)
{
// Элемент в кеше не найден; значит,
// загружаем из базы данных
LoadCustomerFromDb(cust);
// Теперь добавляем этот объект к кешу
// для будущих обращений
Cache.Insert(key, cust, null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default, null );
}
return cust;
}
Распределенный кеш в памяти может быть быстрее и более масштабируемым, чем реляционная база данных. В табл. 1 показаны некоторые данные по производительности, которые дадут вам общее представление на этот счет. Как видите, масштабируемость линейна. Сравните это со своей реляционной базой данных или с хранилищем ASP.NET Session State и вы сразу заметите выигрыш.
Табл. 1. Показатели производительности для типичного распределенного кеша
Размер кластера | Операций чтения в секунду | Операций записи в секунду |
2 узла | 50,000 | 32,000 |
3 узла | 72,000 | 48,000 |
4 узла | 72,000 | 64,000 |
5 узлов | 120,000 | 80,000 |
6 узлов | 144,000 | 96,000 |
Кеширование состояния сеанса, состояния представления и вывода страниц ASP.NET
Использование распределенного кеша в памяти в Azure также помогает в случае ASP.NET Session State, ASP.NET View State и ASP.NET Output Cache. Вам понадобится где-то хранить ASP.NET Session State. Это может стать главным узким местом при масштабировании. В Azure вы можете хранить ASP.NET Session State в базе данных SQL, Azure Table Storage или в распределенном кеше в памяти.
База данных SQL не идеальна для хранения состояния сеанса. Реляционные базы данных никогда не рассчитывали под хранение Blob (больших двоичных объектов), а объект ASP.NET Session State хранится как Blob. Это может вызвать проблемы с производительностью и стать узким местом для масштабируемости.
Аналогично Azure Table Storage не идеально для хранения Blob. Оно предназначено для хранения структурированных сущностей. Хотя оно лучше масштабируется, чем база данных SQL, это все равно не лучшее место для хранения ASP.NET Session State.
Распределенный кеш в памяти лучше подходит для хранения ASP.NET Session State в Azure. Он быстрее и более масштабируемый, чем остальные два варианта. Он также обеспечивает репликацию сеансов, поэтому при падении сервера кеша никакой потери данных не будет. Если вы храните сеансы в выделенном уровне кеширования, тогда веб-роли и виртуальные машины веб-серверов начинают работать без состояний, что хорошо, поскольку вы можете остановить их без потери данных сеансов.
Хотя выполнение ASP.NET Session State в кеше идеально с точки зрения производительности, если кеш рухнет, рухнет и все ваше приложение. И конечно, будет потеряно все, что было в вашем сеансе. Новый провайдер состояния сеанса Redis Cache for Azure предоставит вам возможность узнавать, когда происходят проблемы таких видов, и, по крайней мере, отображать их пользователю понятным ему способом.
На рис. 2 показано, как сконфигурировать распределенный кеш в памяти на хранение ASP.NET Session State.
Рис. 2. Конфигурирование хранилища ASP.NET Session State в распределенном кеше
{Для верстки: не код, а чушь какая-то. Явно кто-то в оригинале перепутал и влепил просто дубликат кода с рис. 1. Идиоты. Мне неоткуда взять нормальный код, так что пусть остается на их совести.}
// Проверяем кеш перед обращением к базе данных
Customer Load(string customerId)
{
// Ключ (key) будет чем-то вроде Customer:PK:1000
string key = "Customers:CustomerID:" + customerId;
Customer cust = (Customer)Cache[key];
if (cust == null)
{
// Элемент в кеше не найден; значит,
// загружаем из базы данных
LoadCustomerFromDb(cust);
// Теперь добавляем этот объект к кешу
// для будущих обращений
Cache.Insert(key, cust, null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default, null );
}
return cust;
}
Хотя инфраструктура ASP.NET MVC избавила от необходимости использовать ASP.NET View State, большинство приложений ASP.NET пока не перенесены на инфраструктуру ASP.NET MVC. Поэтому они по-прежнему требуют поддержки ASP.NET View State.
ASP.NET View State может быть тяжким бременем для пропускной способности и вызывать заметное увеличение времени ответа в вашем приложении ASP.NET. Это связано с тем, что ASP.NET View State может иметь размер в несколько сотен килобайт для каждого пользователя. Это состояние необязательно посылать в браузер и от браузера при обратной передаче формы (post back). Если ASP.NET View State кешируется на уровне веб-серверов и браузеру отправляется уникальный идентификатор, это может сократить время ответа и уменьшить использование полосы пропускания.
Распределенный кеш в памяти лучше подходит для хранения ASP.NET Session State в Azure.
В Azure, где ваше приложение ASP.NET выполняется в нескольких веб-ролях или VM, наименее деструктивное место для кеширования этого ASP.NET View State — распределенный кеш в памяти. Тогда вы обращаетесь к нему и любого веб-сервера. Вот как сконфигурировать ASP.NET View State для хранения в распределенном кеше в памяти:
<!-- /App_Browsers/Default.browser -->
<browsers>
<browser refID="Default" >
<controlAdapters>
<adapter
controlType="System.Web.UI.Page"
adapterType="DistCache.Adapters.PageAdapter"/>
</controlAdapters>
</browser>
</browsers>
ASP.NET также предоставляет инфраструктуру кеширования вывода страниц (output cache framework), которая позволяет кешировать вывод страниц, которые скорее всего не изменятся. Тем самым вам не придется в следующий раз снова выполнять одну из таких страниц. Это экономит процессорные ресурсы и ускоряет ответ приложения ASP.NET. В конфигурации с несколькими серверами лучшее место для кеширования вывода страниц — внутри распределенного кеша, чтобы этот вывод был доступен со всех веб-серверов. К счастью, ASP.NET Output Cache имеет архитектуру на основе провайдеров, поэтому вы можете легко подключить распределенный кеш в памяти (рис. 3).
Рис. 3. Конфигурирование кеша вывода ASP.NET под распределенный кеш в памяти
<!-- web.config -->
<system.web>
<caching>
<outputCache defaultProvider="DistributedCache">
<providers>
<add name="DistributedCache"
type="Vendor.Web.DistributedCache.DistCacheOutputCacheProvider,
Vendor.Web.DistributedCache"
cacheName="default"
dataCacheClientName="default"/>
</providers>
</outputCache>
</caching>
</system.web>
Совместное использование данных периода выполнения через события
Еще одна причина подумать о применении распределенного кеша в памяти в Azure — совместное использование данных периода выполнения. Приложения, как правило, совместно используют данные периода выполнения следующими способами:
- опрос реляционных баз данных для обнаружения изменений в данных;
- использование событий базы данных (например, SqlDependency или OracleDepedency);
- использование очередей сообщений, таких как MSMQ.
Все эти подходы обеспечивают базовую функциональность, но каждый из них имеет определенные проблемы с производительностью и масштабируемостью. Опрос обычно является плохой идеей. Он требует много операций чтения базы данных, без которых вполне можно обойтись. Базы данных и без того являются узким местом для масштабируемости, даже без дополнительных событий базы данных. С добавлением издержек из-за событий базы данных могут захлебнуться еще быстрее под тяжелой транзакционной нагрузкой.
Очереди сообщений обеспечивают последовательный обмен данными и сохранение событий в постоянном хранилище. Они хороши в ситуациях, где получатели могут подолгу не принимать события или где приложения распределены по WAN. Однако, когда дело доходит до среды с высокой транзакционной активностью, очереди сообщений не позволят выйти на уровень производительности или масштабирования распределенного кеша в памяти.
Эластичность кеша — важный аспект поддержания эффективности распределенного кеша в памяти.
Поэтому, если вы имеете дело со средой с интенсивными транзакциями, где нескольким приложения нужно совместно использовать данными в период выполнения без выстраивания их в последовательность, и если вам не требуется длительно хранить события, то можно подумать об использовании распределенного кеша в памяти для обмена данными периода выполнения. Распределенный кеш в памяти позволяет совместно использовать данные в период выполнения самыми разнообразными способами, и все они являются асинхронными.
- События уровня элемента при обновлении и удалении.
- События уровня кеша и группы/региона.
- События на основе постоянного запроса.
- События на основе Topic (для модели публикации/подписки).
Первые три возможности — это, по сути, разные способы мониторинга изменений данных в кеше. Ваше приложение регистрируется на обратные вызовы для каждого из элементов данных. Распределенный кеш отвечает за «генерацию события» всякий раз, когда соответствующие данные в кеше изменяются. Это приводит к инициации обратного вызова вашего приложения.
Когда обновляется или удаляется определенный элемент кеша, будет сгенерировано событие уровня элемента. События уровня кеша и группы/региона генерируются, когда в этот «контейнер» добавляются данные или же они обновляются либо удаляются. Постоянный запрос (continuous query) состоит из критериев поиска для определения некоего набора данных в распределенном кеше. Распределенный кеш генерирует события всякий раз, когда вы добавляете, обновляете или удаляете данные из этого набора. Благодаря этому вы можете отслеживать изменения в кеше:
string queryString = "SELECT Customers WHERE this.City = ?";
Hashtable values = new Hashtable();
values.Add("City", "New York");
Cache cache = CacheManager.GetCache(cacheName);
ContinuousQuery cQuery = new ContinuousQuery(queryString, values);
cQuery.RegisterAddNotification(
new CQItemAddedCallback(cqItemAdded));
cQuery.RegisterUpdateNotification(
new CQItemUpdatedCallback(cqItemUpdated));
cQuery.RegisterRemoveNotification(
new CQItemRemovedCallback(cqItemRemoved));
cache.RegisterCQ(query);
События на основе Topic являются универсальными и не связаны с какими-либо изменениями данных в кеше. В этом случае клиент кеша отвечает за «генерацию события». Распределенный кеш становится чем-то вроде шины сообщений и транспортирует это событие всем остальным клиентам, подключенным к этому кешу.
В случае событий на основе Topic ваши приложения могут обмениваться данными по модели публикации/подписки, где одно из приложений публикует данные и генерирует событие уровня Topic. Остальные приложения ждут это событие и при его появлении начинают использовать соответствующие данные.
Архитектура распределенного кеша
В приложениях с большим трафиком нельзя допускать простоев. Для таких приложений, выполняемых в Azure, важны три аспекта распределенного кеша в памяти.
- Высокая доступность.
- Линейная масштабируемость.
- Репликация данных и надежность.
Эластичность кеша — важный аспект поддержания эффективности распределенного кеша в памяти. Многие распределенные кеши в памяти достигают эластичности и высокой доступности за счет следующего.
Самовосстанавливающийся кластер кеша с равноправием Все серверы кеша образуют кластер кеша. Самовосстанавливающийся кластер кеша с равноправием (self-healing peer-to-peer cluster) перераспределяет нагрузку всякий раз, когда узлы добавляются или удаляются. Более мощные кеши образуют самовосстанавливающийся кластер кеша с равноправием, а остальные — кластеры по типу «ведущий/ведомые» (master/slave clusters). Равноправный кластер является динамическим и позволяет добавлять или удалять серверы кеша, не останавливая работу кеша. Кластеры «ведущий/ведомые» ограниченны, так как выход из строя одного или более назначенных узлов ухудшает функционирование кеша. Некоторые кеши вроде Memcached не образуют никакого кластера кеша и поэтому не считаются эластичными.
Аварийное переключение Клиенты кеша — это приложения, выполняемые на серверах приложений или веб-серверах, которые получают доступ к серверам кеша. Аварийное переключение (connection failover) поддерживается в рамках клиентов кеша. То есть, если какой-нибудь сервер кеша в кластере выходит из строя, клиент кеша продолжит работать, найдя другие серверы в кластере.
Динамическая конфигурация Эта возможность поддерживается как серверами кеша, так и клиентами кеша. Вместо требования к клиентам кеша «зашивать» в код детали конфигурации серверы кеша распространяют эту информацию клиентам кеша в период выполнения, в том числе сообщают о любых изменениях.
Топологии кеширования
Во многих случаях вы кешируете данные, которые отсутствуют в базе данных, например ASP.NET Session State. Поэтому потеря этих данных может оказаться весьма неприятной. Даже если эти данные имеются в базе данных, потеря большей их части из-за выхода из строя узла кеша может сильно ухудшить производительность приложения.
Таким образом, лучше, если ваш распределенный кеш в памяти реплицирует данные. Однако репликация влечет за собой издержки в плане производительности и пространства для хранения. Хороший кеш в памяти предоставляет набор топологий кеширования для обработки различных требований к хранилищу данных и репликации.
Хороший кеш в памяти предоставляет набор топологий кеширования для обработки различных требований к хранилищу данных и репликации.
Кеш с зеркалом (mirrored cache) В этой топологии один активный и один пассивный серверы кеша. Все операции чтения и записи выполняются в активном узле, а обновления асинхронно применяются к зеркалу. Эта топология обычно применяется, когда вы можете обойтись только одним выделенным сервером кеша и обмениваться данными с сервером приложений/веб-сервером как с зеркалом.
Кеш с репликацией (replicated cache) В этой топологии имеются два или более активных сервера кеша. Весь кеш реплицируется на них всех. Все обновления синхронные — они применяются ко всем серверам кеша как одна операция. Транзакции чтения линейно масштабируются по мере добавления серверов. Недостаток заключается в том, что добавление узлов не увеличивает хранилище или пропускную способность обработки транзакций обновления.
Кеш с разделами (partitioned cache) В этой топологии весь кеш делится на разделы, причем каждый сервер кеша содержит один раздел. Клиенты кеша обычно подключаются ко всем серверам кеша, чтобы можно было напрямую обращаться к данным в нужном разделе. Ваше хранилище и пропускная способность по транзакциям растут по мере добавления серверов, так что масштабируемость носит линейный характер. Однако здесь нет репликации, поэтому вы можете потерять данные, если один из серверов кеша выйдет из строя.
Кеш с разделами и репликацией (partitioned-replicated cache) Аналог кеша с разделами, но каждый раздел реплицируется минимум на еще один сервер кеша. Вы не потеряете никакие данные, если один из серверов кеша выйдет из строя. Раздел обычно активен, а реплика пассивна. Ваше приложение никогда не взаимодействует напрямую с репликой. Эта топология дает преимущества кеша с разделами (линейную масштабируемость и др.) плюс надежность хранения данных. Создает небольшие издержки в плане производительности и пространства хранилища, связанные с репликацией.
Кеш клиента (или Near Cache) Клиенты кеша обычно выполняются на серверах приложений или веб-серверах, поэтому обращение к кешу обычно создает сетевой трафик. Кеш клиента (также называемый Near Cache) является локальным, в котором часто используемые данные хранятся близко к вашему приложению и не требуют передачи данных по сети. Этот локальный кеш также соединяется и синхронизируется с распределенным кешем. Кеш клиента может быть внутрипроцессным (InProc) (т. е. находиться внутри процесса вашего приложения) или внепроцессным (OutProc).
Развертывание распределенного кеша в Azure
В Azure имеется несколько распределенных кешей, в том числе Microsoft Azure Cache, NCache for Azure и Memcached. Каждый кеш предоставляет разный набор вариантов. Ниже перечислены наиболее распространенные варианты развертывания для одного региона (если не указано иное).
- Кеш внутри роли (In-Role Cache) (совмещенной или выделенной).
- Сервис кеша (Cache Service).
- Виртуальные машины кеша (Cache VM) (выделенные).
- Виртуальные машины кеша через WAN (несколько регионов).
Кеш внутри роли Вы можете развернуть такой кеш в совмещенной (co-located) или выделенной роли в Azure. Под совмещенной ролью подразумевается, что ваше приложение тоже выполняется в этой VM, а под выделенной — что выполняется только кеш. Хотя хороший распределенный кеш обеспечивает эластичность и высокую доступность, с добавлением или удалением серверов кеша из кластера кеша связаны издержки. Вы должны отдавать предпочтение стабильному кластеру кеша. Добавлять или удалять серверы кеша следует, только когда вам нужно масштабирование или сокращение емкости кеша или когда какой-то сервер кеша выходит из строя.
Кеш внутри роли более неустойчивый, чем другие варианты развертывания, потому что Azure может легко запускать и останавливать роли. В совмещенной роли кеш вдобавок использует процессорные ресурсы и память, общие с вашими приложениями. Для одного или двух экземпляров этот вариант приемлем. Однако для более масштабных развертываний он не годится из-за негативного влияния на производительность.
Кроме того, вы можете подумать о применении выделенного кеша внутри роли. Имейте в виду, что этот кеш развертывается как часть вашего облачного сервиса и видим только внутри этого сервиса. Вы не сможете сделать этот кеш общим для нескольких приложений. Кеш работает до тех пор, пока выполняется ваш сервис. Так что, если вам нужен кеш, выполняемый, даже когда ваше приложение останавливается, не используйте этот вариант.
Microsoft Azure Cache и NCache for Azure предлагают вариант развертывания внутри роли. На эту же конфигурацию с некоторыми усилиями можно настроить и Memcached, но вы потеряете данные при повторном использовании роли, так как Memcached не реплицирует данные.
Сервис кеша В этом случае распределенный кеш развертывается независимо от вашего сервиса и предлагает представление уровня кеша (cache-level view). Вы выделяете определенное количество памяти и процессорных ресурсов и создаете свой кеш. Преимущество сервиса кеша — его простота. Вы не устанавливаете и не конфигурируете самостоятельно никакое программное обеспечение распределенного кеша. В итоге управление кешем упрощается, поскольку Azure берет на себя управление кластером распределенного кеша. Другое преимущество в том, что вы можете сделать этот кеш общим между несколькими приложениями.
Недостаток сервиса кеша — ограниченный доступ. Вы не можете контролировать или манипулировать VM кеша в кластере, как в случае распределенного кеша на предприятии. Кроме того, вы не можете развернуть никакой код на серверной стороне вроде Read-through (сквозное чтение), Write-through (сквозная запись), загрузчика кеша и т. д. У вас не будет контроля за количеством VM кеша в кластере, так как это будет делать сама Azure. Вы можете лишь выбрать вариант развертывания: Basic, Standard или Premium. Microsoft Azure Cache предоставляет вариант развертывания Cache Service, а NCache for Azure — нет.
VM кеша Другой вариант — развернуть свой распределенный кеш в Azure. Это дает вам полный контроль над этим кешем. Развертывая приложение в веб-ролях, рабочих ролях или выделенных VM, вы можете развернуть и клиентскую часть распределенного кеша (библиотеки). Кроме того, можно установить клиент кеша через Windows Installer при создании роли. Это дает вам больше вариантов установки, например OutProc Client Cache (Near Cache).
Затем вы можете создать выделенные VM как серверы своего кеша, установить программное обеспечение распределенного кеша и сформировать кластер кеша на основе потребностей. Эти выделенные VM кеша стабильны и продолжают работать, даже когда ваше приложение останавливается. Ваш клиент кеша взаимодействует с кластером кеша по протоколу TCP. В случае Cache VM вы можете использовать два сценария развертывания.
- Развертывание в виртуальной сети Ваши прикладные роли/VM и VM кеша находятся в границах одной виртуальной сети. Никаких конечных точек между вашими приложением и распределенным кешем нет. В итоге доступ к кешу выполняется быстро. Кеш также полностью скрыт от внешнего мира и поэтому более защищен.
- Развертывание в отдельных виртуальных сетях Ваши прикладные роли/VM и VM кеша находятся в разных виртуальных сетях. Распределенный кеш содержится в собственной виртуальной сети и предоставляется через конечные точки. В результате вы можете использовать этот кеш из разных приложений и регионов. Однако вы получаете гораздо больший контроль через Cache VM, чем через Cache Service.
В обоих вариантах развертывания Cache VM вы получаете полный доступ ко всем серверам кеша. Это позволяет развертывать код на серверной стороне так же, как и на предприятии. Вы также получаете больше информации мониторинга, поскольку имеете полный доступ к VM кеша. Microsoft Azure Cache не предоставляет вариант Cache VM, но он доступен в NCache for Azure и Memcached.
Microsoft планирует сделать доступным свой управляемый кеш к июлю, который заменит существующий сервис общего кеша (shared cache service). Скорее всего у него не будет портала Azure, и он потребует работы с командной строкой Windows PowerShell для создания и управления кешем.
Вы можете установить NCache for Azure в выделенных VM и обращаться к ним из своих веб- и рабочих ролей. Вы также можете установить NCache for Azure в веб- и рабочих ролях, но эта стратегия не рекомендуется. NCache for Azure не является сервисом кеша. В Microsoft Azure имеется Redis Cache Service, доступный через портал управления.
VM кеша через WAN Если у вас есть облачный сервис, развернутый в нескольких регионах, подумайте о развертывании VM кеша через WAN. Развертывание Cache VM в каждом регионе — то же самое, что и предыдущий вариант. Однако у вас возникает ситуация с двумя дополнительными требованиями.
- Сеансы ASP.NET нескольких сайтов Вы можете передавать сеанс ASP.NET с одного сайта на другой при перенаправлении пользователя. Это часто происходит в приложениях, развернутых на нескольких активных сайтах. Они могут перенаправлять трафик для распределения чрезмерной нагрузки или из-за аварии одного из сайтов.
- Репликация кеша в WAN Вы можете реплицировать все обновления кеша с одного сайта на другой. Это возможно при развертывании на нескольких сайтах в вариантах «активный-пассивный» и «активный-активный». В первом случае обновления реплицируются в одном направлении, а во втором — в обоих. Кеш поддерживается в синхронизированном состоянии между несколькими сайтами через WAN-репликацию, но учтите, что это занимает полосу пропускания.
Важные функции кеширования
Когда вы используете распределенный кеш в памяти, он обрабатывает огромные объемы данных. Базовый распределенный кеш в памяти предоставляет лишь интерфейс hashtable (key, value). Распределенный кеш корпоративного уровня может дополнительно предоставлять следующие функции.
Функции поиска Вместо того чтобы всегда искать данные на основе ключа, гораздо проще вести поиск в кеше по некоторым другим логическим данным. Например, многие распределенные кеши позволяют использовать группы и теги для логического группирования кешируемых элементов. Многие также дают возможность вести поиск по кешу через LINQ, популярную при поиске объектов в .NET Framework. Некоторые предоставляют собственный Object Query Language (OQL) — SQL-подобный язык запросов, с помощью которого вы можете искать нужные данные в кеше. Возможность поиска в распределенном кеше, основанная на атрибутах, а не ключах, делает такой кеш очень похожим на реляционную базу данных. На рис. 4 показано, как можно выполнять подобный поиск.
Рис. 4. Настройка поиска в распределенном кеше через LINQ
public IList<Customer> GetCutomers(string city)
{
//Используем LINQ для поиска в распределенном кеше
IQueryable<Customer> customers =
new DistCacheQuery<Customer>(cache);
try {
var result = from customer in customers
where customer.City = city
select customer;
IList<Customer> prods = new List<Customer>();
foreach (Customer cust in result) {
customers.Add(cust);
}
}
return customers;
}
Обработчики сквозного чтения и сквозной/отложенной записи Обработчики сквозного чтения и записи упрощают код вашего приложения, так как вы перемещаете большую часть кода сохранения в кластер кеша. Приложение считает кеш своим хранилищем данных. Это другой способ повторного использования кода между несколькими приложениями.
Кеш вызывает обработчик сквозного чтения всякий раз, когда ваше приложение пытается извлечь что-то, чего нет в кеше (такая ситуация называется промахом кеша). В случае промаха кеш вызывает обработчик сквозного чтения, и тот извлекает данные из вашей базы данных (рис. 5). Распределенный кеш помещает их в кеш и возвращает вашему приложению.
Рис. 5. Обработчик сквозного чтения для распределенного кеша
public class SqlReadThruProvider : IReadhThruProvider
{
//Вызывается при запуске для инициализации соединения
public void Start(IDictionary parameters) { ... }
// Вызывается в конце для закрытия соединения
public void Stop() { ... }
// Отвечает за загрузку объекта из внешнего источника данных
public object Load(string key, ref CacheDependency dep)
{
string sql = "SELECT * FROM Customers WHERE ";
sql += "CustomerID = @ID";
SqlCommand cmd = new SqlCommand(sql, _connection);
cmd.Parameters.Add("@ID", System.Data.SqlDbType.VarChar);
// Извлекает customerID из ключа
int keyFormatLen = "Customers:CustomerID:".Length;
string custId = key.Substring(keyFormatLen,
key.Length - keyFormatLen);
cmd.Parameters["@ID"].Value = custId;
// Считывает строку таблицы
SqlDataReader reader = cmd.ExecuteReader();
// Копирует данные из reader в объект cust
Customers cust = new Customers();
FillCustomers(reader, cust);
// Указываем SqlCacheDependency для этого объекта
dep = new SqlCacheDependency(cmd);
return cust;
}
}
Кеш также вызывает ваш обработчик сквозной записи всякий раз, когда вы обновляете кеш и хотите, чтобы он автоматически обновлял базу данных. Обработчик выполняется на одном или более серверах кеша в кластере и взаимодействует с базой данных. Однако, если сквозная запись вызывается асинхронно после некоей задержки и не является частью транзакции обновления кеша, эта операция считается отложенной записью (write-behind). Отложенная запись увеличивает производительность приложения, так как вам не приходится ждать окончания обновления базы данных.
Синхронизация кеша с реляционной базой данных Большинство данных в распределенном кеше поступает из базы данных вашего приложения. Это подразумевает, что теперь существуют две копии данных: мастер-копия в базе данных и копия в распределенном кеше. Если у вас есть приложения, напрямую обновляющие данные в базе данных, но не имеющие к вашему распределенному кешу в памяти, то дело кончится тем, что в кеше окажутся неактуальные данные.
Некоторые распределенные кеши обеспечивают синхронизацию с базой данных, гарантируя, что в кеше никогда не будет устаревших данных. Эта синхронизация либо инициируется событиями (с использованием таких уведомлений баз данных, как SqlDependency), либо базируется на опросе. Первый вариант ближе к режиму реального времени, но приводит к более высоких издержкам, поскольку создает отдельный SqlDependency на сервере базы данных для каждого кешируемого элемента. Опрос эффективнее, так как одним вызовом базы данных можно синхронизировать тысячи элементов. Но обычно перед синхронизацией устанавливается некая задержка, чтобы избежать перегрузки базы данных ненужными опрашивающими вызовами. Так что ваш выбор лежит между более близкой к режиму реального времени синхронизацией с базой данных и более эффективной синхронизацией на основе опроса с некоторой задержкой.
Обработка реляционных данных в распределенном кеше Распределенный кеш в памяти является хранилищем объектов со структурой по принципу «ключ-значение», но большинство кешируемых данных поступает из реляционной базы данных. Эти данные имеют отношения «один ко многим», «один к одному» и «многие ко многим». Реляционные базы данных обеспечивают ограничения ссылочной целостности и каскадные обновления/удаления для введения в действие этих отношений. Кешу требуется нечто подобное.
CacheDependency позволяет сделать так, чтобы один кешируемый элемент зависел от другого. Если другой кешируемый элемент обновляется или удаляется, то исходный кешируемый элемент автоматически удаляется. Эта операция подобна каскадному удалению. Она полезна для обработки отношений «один к одному» и «один ко многим» между объектами в кеше. В некоторых распределенных кешах в памяти реализована и эта функция. На рис. 6 показано, как вы могли бы настроить CacheDependency.
Рис. 6. Управление отношениями в кеше с помощью CacheDependency
public void CacheCustomerAndOrder(Customer cust, Order order)
{
Cache cache = HttpRuntime.Cache;
// Объект Customer имеет отношение "один ко многим" с Order.
// Сначала поместите в кеш Customer, затем Order и создайте
// CacheDependency от Customer.
string custKey = "Customer:CustomerId:" + cust.CustomerId;
cache.Add(custKey, cust, null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.Default, null);
//CacheDependency гарантирует удаление order,
// если Cust обновляется или удаляется
string[] keys = new string[1];
keys[0] = custKey;
CacheDependency dep = new CacheDependency(null, keys);
string orderKey = "Order:CustomerId:" + order.CustomerId
+ ":ProductId:" + order.ProductId;
// Это приведет к кешированию объекта Order
// с CacheDependency от customer
cache.Add(orderKey, order, dep,
absolutionExpiration,
slidingExpiration,
priority, null);
}
Заключение
Microsoft Azure — мощная платформа облака и масштабируемая среда. Распределенный кеш в памяти может быть важным компонентом этой среды. Если вы пишете свои приложения в среде .NET Framework, вам следует подумать об использовании распределенного .NET-кеша. Если вы делаете это на Java, у вас есть выбор из разнообразных Java-решений по распределенному кешированию. Если у вас имеется комбинация приложений .NET и Java, используйте распределенный кеш, который поддерживает обе среды и обеспечивает портируемость данных. Большинство кешей полностью сфокусированы на .NET или Java, хотя некоторые поддерживают обе среды.
В случае приложений на PHP, Ruby, Python и других языках можно задействовать Memcached. Он поддерживает все эти среды. Memcached не является эластичным кешем и поэтому имеет ограничения по высокой доступности и надежности данных. В любом случае помните, что распределенный кеш является жизненно важной частью вашей производственной среды. Таким образом, вы должны тщательно оценить все доступные для вашей среды решения по кешированию и выбрать то, что лучше всего подходит под ваши потребности.