n-tier: на каком из уровнях кэшировать данные?
От: AK107  
Дата: 14.05.11 11:00
Оценка:
Схема приложения:
клиент(1) -> службы (2) -> службы доступа к данным(3) -> БД


Где правильней кэшировать данные: на уровне 2 или на уровне 3?
Re: n-tier: на каком из уровнях кэшировать данные?
От: iZEN СССР  
Дата: 14.05.11 11:05
Оценка: 6 (1) +1
Здравствуйте, AK107, Вы писали:

AK>Схема приложения:
AK>клиент(1) -> службы (2) -> службы доступа к данным(3) -> БД
AK>


AK>Где правильней кэшировать данные: на уровне 2 или на уровне 3?


В том или ином виде на всех уровнях НУЖНО кэшировать данные.
Re: n-tier: на каком из уровнях кэшировать данные?
От: Lloyd Россия  
Дата: 14.05.11 11:16
Оценка: 5 (1) +1
Здравствуйте, AK107, Вы писали:

AK>Схема приложения:
AK>клиент(1) -> службы (2) -> службы доступа к данным(3) -> БД
AK>


AK>Где правильней кэшировать данные: на уровне 2 или на уровне 3?


Зависит.


Step 2 – Determine Where to Cache Data


When deciding on where to cache data, there are typically two things you must consider: the physical location of the cache, and the logical location of the cache.

The physical location will either be in-memory, or disk-based using files or a data-base. In-memory caching may be performed using the ASP.NET cache mechanism, Enterprise Library Caching Application Block, or a distributed in-memory caching mechanism such as Microsoft project code named “Velocity” or the Danga Interactive “Memcached” technology. An in-memory cache is a good choice when the data is used frequently by the application, the cached data is relatively volatile and must be frequently reacquired, and the volume of cached data is relatively small. A file system-based or database cache is a good choice when accessing data from the cache store is more efficient when compared to acquiring the data from the original store, the cached data is relatively less volatile, and the services for reacquiring the data are not always available. The disk-based approach is also ideal when the volume of cached data is relatively large, or the cached data must survive process and machine restarts.

The logical location of the cache describes the location within the application logic. It is important to cache the data as close as possible to the location where it will be used to minimize processing and network round trips, and to maximize the performance and responsiveness of the application. Consider the following guidelines when deciding on the logical location of the cache data:

* Consider caching on the client when the data is page specific or user specific, does not contain sensitive information, and is lightweight.

* Consider caching on a proxy server or Web server (for Web applications) when you have relatively static pages that are requested frequently by clients, your pages are updated with a known frequency, or the results are returned from Web services. Also, consider this approach where you have pages that can generate different output based on HTTP parameters, and those parameters do not often change. This is particularly useful when the range of outputs is small.

* Consider caching data in the presentation layer when you have relatively static page outputs, you have small volumes of data related to user preferences for a small set of users, or you have UI controls that are expensive to create. Also consider this approach when you have data that must be displayed to the user and is expensive to create; for example, product lists and product information.

* Consider caching data in the business layer when you must maintain state for a service, business process, or workflow; or when relatively static data is required to process requests from the presentation layer and this data is expensive to create.

* Consider caching data in the data layer when you have input parameters for a frequently called stored procedure in a collection, or you have small volumes of raw data that are returned from frequently executed queries. Consider caching schemas for typed datasets in the data layer.

* Consider caching in a separate table inside the database any data that requires considerable query processing to obtain the result set. This may also be appropriate when you have very large volumes of data to cache, where you implement a paging mechanism to read sections of the data for display in order to improve performance.

Re: n-tier: на каком из уровнях кэшировать данные?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.05.11 13:18
Оценка: +1
Здравствуйте, AK107, Вы писали:

AK>Схема приложения:
AK>клиент(1) -> службы (2) -> службы доступа к данным(3) -> БД
AK>


AK>Где правильней кэшировать данные: на уровне 2 или на уровне 3?


а)надо кешировать везде
б)что кешировать зависит от данных
Re[2]: n-tier: на каком из уровнях кэшировать данные?
От: dimgel Россия https://github.com/dimgel
Дата: 15.05.11 13:21
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>а)надо кешировать везде


А я кеширую только в DAL. Мне так легче следить, чтобы вовремя сбрасывать/корректировать кеши, когда один и тот же DAL из разных BL цепляется. Локализация ответственности, в общем.
Re[3]: n-tier: на каком из уровнях кэшировать данные?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.05.11 13:36
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, gandjustas, Вы писали:


G>>а)надо кешировать везде


D>А я кеширую только в DAL. Мне так легче следить, чтобы вовремя сбрасывать/корректировать кеши, когда один и тот же DAL из разных BL цепляется. Локализация ответственности, в общем.


Чтобы кешировать на уровне DAL нужно чтобы в большая часть запросов была — получение объекта по Id. Иначе как эффективно кешировать — хз.

Когда писал на ASP.NET MVC у меня получалось очень мало таких запросов, не более 10%, остальные были с джоинами, проекциями и хитрыми фильтрами. В таких условиях гораздо эффективнее оказалось кешировать готовые объекты. или их части, передаваемые во view.

А с когерентностью кешей обходился очень легко: вместе с данными в кеш записывал время генерации объекта, это время передавалось в LastModified, а при получении if-modified-since проверялось по значению в кеше. Каждое действие, которое может запороть кеш по поределенному ключу, просто очищает его и все.
Re[4]: n-tier: на каком из уровнях кэшировать данные?
От: dimgel Россия https://github.com/dimgel
Дата: 15.05.11 14:48
Оценка:
Здравствуйте, gandjustas, Вы писали:

D>>А я кеширую только в DAL. Мне так легче следить, чтобы вовремя сбрасывать/корректировать кеши, когда один и тот же DAL из разных BL цепляется. Локализация ответственности, в общем.


G>Чтобы кешировать на уровне DAL нужно чтобы в большая часть запросов была — получение объекта по Id. Иначе как эффективно кешировать — хз.


G>Когда писал на ASP.NET MVC у меня получалось очень мало таких запросов, не более 10%, остальные были с джоинами, проекциями и хитрыми фильтрами. В таких условиях гораздо эффективнее оказалось кешировать готовые объекты. или их части, передаваемые во view.


Угу, у меня большая часть — by id. Но я не стесняюсь кешировать результаты join-ов и иногда даже списки. (Хотя тут ограничения, см. ниже.) Например, в одном проекте у меня есть для каждого юзера список необработанных им извещений:

package model
case class Section(id: Int, ownerId: Int)
case class Nofification (id: Int, postId: Int, sectionId: Int)

package modelx
case class SectionX (o: Section, owner: User)
case class NofificationX (o: Notification, post: Post, sectionX: SectionX)

object NotificationDAL {
    private val byUserIdCache = new MapCache[Int, List[NotificationX]](maxElements_? = Some(10))
    def getForUser(userId: Int) = byUserIdCache(userId, userId => {
        // запрос (notification join post join (section join user)),
        // возвращающий List[NotificationX]
    })
}


Но гораздо чаще (особенно если FK на условно постоянный справочник), чтобы не париться с когерентностью, я завожу отдельный SectionDAL с byId-кешем и вместо notificationX.section пишу SectionDAL.get(notification.sectionId).

G>А с когерентностью кешей обходился очень легко: вместе с данными в кеш записывал время генерации объекта, это время передавалось в LastModified, а при получении if-modified-since проверялось по значению в кеше. Каждое действие, которое может запороть кеш по поределенному ключу, просто очищает его и все.


Просто очищает кого — LastModified в кеше? Вообще, не очень понял область применимости. Если речь просто об отдаче ресурса через HTTP или ещё как, то тут понятно. Но если речь идёт о сложной обработке — ??? Вот пример: добавление камента в блог. Такой вот отдельный метод в BL. (У меня BL могут обращаться к другим BL и к DAL, так что тут всё в шаговой доступности, по задумке.) Нужно проверять, забанен ли юзер, добавляющий комментарий (либо вообще каменты в данный блог запрещены), скринятся ли его каменты (либо вообще все каменты в данном блоге), потом обработать всех подписавшихся на этот блог и т.п. На такой простой фигне вылезает масса взаимосвязанных аспектов. Нужно собрать кучу разнородных данных из разных мест, где эти данные обновляются независимо друг от дружки. Вот и получается, что в результате я кеширую всё по-отдельности (вместо составных объектов modelx), потому что одни и те же данные могут потребоваться в разных BL-сценариях, и чем более крупногранулированные кеши, тем больше шанс либо не найти в кешах нужных данных для какого-либо специфического сценария, либо получить задвоение одних и тех же данных в разных кешах. И каким боком тут можно приткнуть твою идею, я тоже пока недопонял: какой timestamp передавать кешам в описанном мною сценарии в качестве параметра if-modified-since. Ещё помедитирую попозже, а если чего распишешь поконкретнее, буду благодарен.

Может кстати показаться, что я в итоге ничего не экономлю: не так давно обсуждали, что затраты на материализацию нынче пустяковые. Но тем не менее полгода назад, когда я делал lib.cache, поставил простенький эксперимент. На тогдашнем сайте был у меня список статей. Страница просмотра одной статьи запрашивала из базы статью по коду, текущего юзера по коду (из сессии), главное меню сайта, и может ещё чего. Я добавил только один кеш — ArticleDAL.byId, и скорость отдачи страницы выросла в 10 раз — даже с локальным PostgreSQL. Замерял через wget в длинном цикле; 800 страниц в секунду против 80 без кеша.
Re[4]: n-tier: на каком из уровнях кэшировать данные?
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.05.11 14:50
Оценка:
Здравствуйте, gandjustas, Вы писали:


G>Чтобы кешировать на уровне DAL нужно чтобы в большая часть запросов была — получение объекта по Id. Иначе как эффективно кешировать — хз.

Ну, я еще в девяностых читал статью про semantic caching, где локальный кэш был SQL-aware, и даже умел вычислять покрываемость предикатов. Ну, то есть если у него был результат для select * from students where age > 20, то он был в состоянии обслужить запрос select * from students where age > 25 из кэша.

G>Когда писал на ASP.NET MVC у меня получалось очень мало таких запросов, не более 10%, остальные были с джоинами, проекциями и хитрыми фильтрами. В таких условиях гораздо эффективнее оказалось кешировать готовые объекты. или их части, передаваемые во view.

Ну, теоретически, способностей нижележащей платформы (SQL Server) уже должно быть достаточно для того, чтобы эффективно кэшировать результаты у себя. От приложения требуется аккуратно выставлять Last-Modified и отдавать 304 Not Modified.

G>А с когерентностью кешей обходился очень легко: вместе с данными в кеш записывал время генерации объекта, это время передавалось в LastModified, а при получении if-modified-since проверялось по значению в кеше. Каждое действие, которое может запороть кеш по поределенному ключу, просто очищает его и все.

+1. Вполне взрослый подход; скорее всего выжимает 95% возможной эффективности кэширования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: n-tier: на каком из уровнях кэшировать данные?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.05.11 15:23
Оценка:
Здравствуйте, dimgel, Вы писали:

G>>А с когерентностью кешей обходился очень легко: вместе с данными в кеш записывал время генерации объекта, это время передавалось в LastModified, а при получении if-modified-since проверялось по значению в кеше. Каждое действие, которое может запороть кеш по поределенному ключу, просто очищает его и все.


D>Просто очищает кого — LastModified в кеше?

Нет, просто грохает запись кеша.

D>Вообще, не очень понял область применимости. Если речь просто об отдаче ресурса через HTTP или ещё как, то тут понятно. Но если речь идёт о сложной обработке — ???

Речь идет обо всем сразу. Такой подход кеширует данные на сервере и клиенте одновременно.

D>Вот пример: добавление камента в блог. Такой вот отдельный метод в BL. (У меня BL могут обращаться к другим BL и к DAL, так что тут всё в шаговой доступности, по задумке.) Нужно проверять, забанен ли юзер, добавляющий комментарий (либо вообще каменты в данный блог запрещены), скринятся ли его каменты (либо вообще все каменты в данном блоге), потом обработать всех подписавшихся на этот блог и т.п. На такой простой фигне вылезает масса взаимосвязанных аспектов. Нужно собрать кучу разнородных данных из разных мест, где эти данные обновляются независимо друг от дружки. Вот и получается, что в результате я кеширую всё по-отдельности (вместо составных объектов modelx), потому что одни и те же данные могут потребоваться в разных BL-сценариях, и чем более крупногранулированные кеши, тем больше шанс либо не найти в кешах нужных данных для какого-либо специфического сценария, либо получить задвоение одних и тех же данных в разных кешах. И каким боком тут можно приткнуть твою идею, я тоже пока недопонял: какой timestamp передавать кешам в описанном мною сценарии в качестве параметра if-modified-since. Ещё помедитирую попозже, а если чего распишешь поконкретнее, буду благодарен.


Ну для примера с блогом. При добавлении коммента нужно обновить:
1)Страницу поста, делаем cache.Remove("blog_post_display_{id}"), кстати этого можно не делать, а обновить прям в объекте.
2)Страницу пользователя, так как на ней выводится количество комментов cache.Remove("blog_user_display_{id}"), правда с высокой долей вероятности она не будет кешироваться
3)Главную страницу блога, если пост в нее попадает. Тут интереснее. Берем cache.Get("blog_display"), в котором список выводимых постов и если там есть текущий пост — удаляем нафиг.


D>Может кстати показаться, что я в итоге ничего не экономлю: не так давно обсуждали, что затраты на материализацию нынче пустяковые. Но тем не менее полгода назад, когда я делал lib.cache, поставил простенький эксперимент. На тогдашнем сайте был у меня список статей. Страница просмотра одной статьи запрашивала из базы статью по коду, текущего юзера по коду (из сессии), главное меню сайта, и может ещё чего. Я добавил только один кеш — ArticleDAL.byId, и скорость отдачи страницы выросла в 10 раз — даже с локальным PostgreSQL. Замерял через wget в длинном цикле; 800 страниц в секунду против 80 без кеша.


Зато ты просадил scalability, ибо при запросе разных страниц у тебя кучи разных объектов будут в кеш и вытеснять друг друга. Может получиться что у тебя для 10мб базы будет 1гб кеш, чтобы он не работал вхолостую.
Re[5]: n-tier: на каком из уровнях кэшировать данные?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.05.11 15:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:



G>>Чтобы кешировать на уровне DAL нужно чтобы в большая часть запросов была — получение объекта по Id. Иначе как эффективно кешировать — хз.

S>Ну, я еще в девяностых читал статью про semantic caching, где локальный кэш был SQL-aware, и даже умел вычислять покрываемость предикатов. Ну, то есть если у него был результат для select * from students where age > 20, то он был в состоянии обслужить запрос select * from students where age > 25 из кэша.

А есть ссылка на это счастье?

G>>Когда писал на ASP.NET MVC у меня получалось очень мало таких запросов, не более 10%, остальные были с джоинами, проекциями и хитрыми фильтрами. В таких условиях гораздо эффективнее оказалось кешировать готовые объекты. или их части, передаваемые во view.

S>Ну, теоретически, способностей нижележащей платформы (SQL Server) уже должно быть достаточно для того, чтобы эффективно кэшировать результаты у себя. От приложения требуется аккуратно выставлять Last-Modified и отдавать 304 Not Modified.

Тут еще важно сократить затраты на передачу от sql server до приложения, что еще и нагрузку на сервер снимет. Это важно для shared-хостинга\cloud.

Я как-бы вообще не стараюсь пихать кеш везде, начинаю с кеширования ответов на самые частые запросы. Обычно после добавления 2-3 кешей на отображаемую сущность повышается скорость во много раз, дальнейшие действия не требуются.
Re[6]: n-tier: на каком из уровнях кэшировать данные?
От: dimgel Россия https://github.com/dimgel
Дата: 15.05.11 15:51
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Зато ты просадил scalability, ибо при запросе разных страниц у тебя кучи разных объектов будут в кеш и вытеснять друг друга. Может получиться что у тебя для 10мб базы будет 1гб кеш, чтобы он не работал вхолостую.


На самом деле читают в основном свежие статьи, так что подгонкой некоего осмысленного размера кеша всё решается. А в моём случае статьи могли редактироваться только владельцами сайта и этих статей было немного (почему у нас лучше чем у других, почему у других хуже чем у нас, да и всё), так что в кеш влезало всё. В общем, it depends, в каждом случае по ситуации надо смотреть.
Re[6]: n-tier: на каком из уровнях кэшировать данные?
От: dimgel Россия https://github.com/dimgel
Дата: 15.05.11 15:59
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Ну для примера с блогом. При добавлении коммента нужно обновить:

G>1)Страницу поста, делаем cache.Remove("blog_post_display_{id}"), кстати этого можно не делать, а обновить прям в объекте.
G>2)Страницу пользователя, так как на ней выводится количество комментов cache.Remove("blog_user_display_{id}"), правда с высокой долей вероятности она не будет кешироваться
G>3)Главную страницу блога, если пост в нее попадает. Тут интереснее. Берем cache.Get("blog_display"), в котором список выводимых постов и если там есть текущий пост — удаляем нафиг.

Целые страницы кешировать? Думал, но отказался — слишком много разнообразного хлама у меня на них, на каждый чих придётся кеш сбрасывать. Блоггеров может быть много и все видят страницу по-разному: "свои" сообщения подсвечены; контролы оценок подсвечены в зависимости от того, какую оценку данный блоггер поставил комментарию; непрочитанные сообщения подсвечены; автор блога видит заскриненные, остальные не видят; и вообще фильтры сообщений могут быть у всех разные... в общем, тут дохлый номер.

Бывают, однако, ситуации, когда имеет смысл кешировать отдельные части страницы — куски разметки. И тут получается хитрый архитектурный вопрос. Кешировать разметку в BL нельзя — BL не должен зависеть от presentation, вдруг там не HTML, а вполне себе десктопный гуй. А если разметку кешировать в web layer, то кто должен отвечать за сброс кешей? Только если через subscriber, чтобы опять же напрямую зависимости BL от web не вводить. Но тогда знание о том, в каких обстоятельствах нужно сбрасывать кеш разметки, размазывается по web и BL.
Re[7]: n-tier: на каком из уровнях кэшировать данные?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.05.11 16:14
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Здравствуйте, gandjustas, Вы писали:


G>>Ну для примера с блогом. При добавлении коммента нужно обновить:

G>>1)Страницу поста, делаем cache.Remove("blog_post_display_{id}"), кстати этого можно не делать, а обновить прям в объекте.
G>>2)Страницу пользователя, так как на ней выводится количество комментов cache.Remove("blog_user_display_{id}"), правда с высокой долей вероятности она не будет кешироваться
G>>3)Главную страницу блога, если пост в нее попадает. Тут интереснее. Берем cache.Get("blog_display"), в котором список выводимых постов и если там есть текущий пост — удаляем нафиг.

D>Целые страницы кешировать?

Не целые страницы, а объекты, которые передаются на view.

D>Думал, но отказался — слишком много разнообразного хлама у меня на них, на каждый чих придётся кеш сбрасывать. Блоггеров может быть много и все видят страницу по-разному: "свои" сообщения подсвечены; контролы оценок подсвечены в зависимости от того, какую оценку данный блоггер поставил комментарию; непрочитанные сообщения подсвечены; автор блога видит заскриненные, остальные не видят; и вообще фильтры сообщений могут быть у всех разные... в общем, тут дохлый номер.

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


D>Бывают, однако, ситуации, когда имеет смысл кешировать отдельные части страницы — куски разметки. И тут получается хитрый архитектурный вопрос. Кешировать разметку в BL нельзя — BL не должен зависеть от presentation, вдруг там не HTML, а вполне себе десктопный гуй. А если разметку кешировать в web layer, то кто должен отвечать за сброс кешей? Только если через subscriber, чтобы опять же напрямую зависимости BL от web не вводить. Но тогда знание о том, в каких обстоятельствах нужно сбрасывать кеш разметки, размазывается по web и BL.


А в чем проблема? Управление куда приходит? В контроллер судя по всему, там и сбрасывать кеш.

Кстати кеш десктопного приложения будет сильно отличаться от кеша для блога, например. В десктопном приложении, кроме identity map или локальной реплики БД мало чего нужно. А вот на сервере в случае высокой нагрузки может быть миллион разных сценариев.
Re[6]: n-tier: на каком из уровнях кэшировать данные?
От: Ziaw Россия  
Дата: 16.05.11 15:35
Оценка:
Здравствуйте, gandjustas, Вы писали:

S>>Ну, я еще в девяностых читал статью про semantic caching, где локальный кэш был SQL-aware, и даже умел вычислять покрываемость предикатов. Ну, то есть если у него был результат для select * from students where age > 20, то он был в состоянии обслужить запрос select * from students where age > 25 из кэша.


G>А есть ссылка на это счастье?


Я бы не назвал это счастьем. Но если очень хочется взгляни на (N)Hibernate query cache.

G>Я как-бы вообще не стараюсь пихать кеш везде, начинаю с кеширования ответов на самые частые запросы. Обычно после добавления 2-3 кешей на отображаемую сущность повышается скорость во много раз, дальнейшие действия не требуются.


+1, 90% проблем производительности этим решаются.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.