Реализация кэширования на уровне модели

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

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

(Источник: Сообщение от Jarrod)

Причина, по которой я настроен скептически, заключается в том, что кеширование обычно не должно выполняться, если нет реальной потребности, и не должно быть сделано для таких вещей, как результаты поиска. Итак, каким-то образом сама Модель должна знать, стоит ли выставлять на нее инструкцию SELECT. Разве Модель не должна быть астрономически умной и/или хранить статистику того, что чаще всего запрашивается в течение длительного периода времени, чтобы точно принять решение? И не будет ли накладные расходы на все это сделать кеширование бесполезным?

Как бы вы однозначно идентифицировали запрос из другого запроса (или, точнее, результат из другого набора результатов)? Что делать, если вы используете подготовленные операторы, только с изменением параметров в соответствии с пользовательским вводом?

Другой плакат сказал это:

Я бы предложил использовать хеш md5 ваш запрос в сочетании с сериализованным версии ваших входных аргументов.

Не стоит ли беспокоиться о минимальной вероятности столкновения?

Концептуально, кэширование в модели кажется для меня хорошей идеей, но, по-видимому, в практичности и из-за характера кеширования разработчик должен иметь прямой контроль над ним и объяснять его код логикой контроллера.


Обновление для Bounty

Я действительно использую чрезвычайно легкий ORM, несколько похожий на ActiveRecord, но способный выполнять сложные объединения и подзапросы без проблемы n^2. Я сам его создал, поэтому он является гибким и не является ограничительным с точки зрения отношений или имен столбцов, и я просто хочу понять, как я должен реализовать механизм кэширования.

Следуя советам полезных людей, я бы взял хэш (возможно, md5) запроса, объединенного со списком его параметров, и использовал его как ключ для этого конкретного хранилища данных. Должен ли я реализовать кеширование отдельно в классах Model, которые его требуют, или он должен быть частью слоя ORM?

Как я узнаю, когда это должно быть признано недействительным? Должен ли я вручную анализировать запросы UPDATE/DELETE/INSERT и вспомогательные параметры, чтобы узнать, какие записи изменяются? Или, что еще хуже, делать дополнительные запросы, когда данные изменяются, чтобы отслеживать, какие вещи изменились и что должно быть недействительным?

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

Ответы

Ответ 1

Ваше сообщение имеет смысл только в том случае, если модель является тривиальной ORM. И есть много причин, почему это плохо. Подумайте о модели, как о веб-службе.

Кэширование - это ответственность модели.

Как бы вы однозначно идентифицировали запрос из другого запроса (или, точнее, результат из другого набора результатов)? Что делать, если вы используете подготовленные операторы, только с изменением параметров в соответствии с пользовательским вводом?

Но входы в модель однозначно определяют его вывод.

Если вы используете ту же модель для извлечения содержимого корзины покупок и для запуска поиска в своем каталоге продуктов, то что-то не так с вашим кодом.

Даже в случае корзины покупок может быть целесообразно кэшировать данные с TTL меньше времени, затраченного на обработку транзакции, которая изменила бы ее содержимое, в случае поиска по каталогу, кэшируя список сопоставимые продукты в течение нескольких часов, вероятно, не окажут заметного влияния на продажи, но также могут значительно снизить нагрузку на базу данных.

Тот факт, что вы используете тривиальный ORM из коробки, не исключает, что вы обертываете его в свой собственный код.

Разве Модель не должна быть астрономически умной и/или хранить статистику

Нет. Вы принимаете решение о том, следует ли кэшировать, и если вы не можете обеспечить согласованность кеша, тогда применяйте TTL на основе типа запроса.

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

Должен ли я реализовать кэширование отдельно в классах модели, которые его требуют, или он должен быть частью уровня ORM?

Для предпочтения я бы реализовал это как декоратор в классе модели - таким образом вы можете легко перенести его в модели, которые реализуют factory, а не тривиальный ORM.

С.

Ответ 2

Существует множество факторов, которые следует учитывать при кешировании, такие как хеширование, аннулирование и т.д. Но цель кеширования всегда одна и та же: уменьшить время отклика и потребление ресурсов.

Вот несколько быстрых мыслей с моей головы для систем, в которых не использовать ORM:

  • Никогда не помешает кешировать что-то с помощью memcache, если у вас есть память для него
  • Вы должны только кэшировать запросы SELECT, поскольку другие типы влияют на данные
  • Все кэшированные запросы должны быть параметризованы
  • Кэш-ключ должен быть md5 запроса, связанного с версией параметров serialize() 'd (это идентифицирует уникальные запросы. Серализация параметров не является проблемой, поскольку размер параметров, обычно передаваемых для выбора запросов, обычно довольно тривиальна). Сериализация не так дорого, как вы думаете. И поскольку вы хешировали свой статический запрос, связанный с вашими динамическими параметрами, вам никогда не придется беспокоиться о столкновениях.
  • Модификации (INSERT/UPDATE/DELETE) для строк в модели должны аннулировать (или установить TTL) для всех элементов, кэшированных для этой модели
  • Модель должна быть расширена, чтобы можно было отправлять значения TTL кэша вместе с запросом
  • Ваша модель должна иметь поддержку для пропуска кеша (возможно, передав TTL 0 вместе с запросом)
  • Несмотря на то, что базовый запрос может быть кэширован, обычно более эффективно применять операции типа ORDER BY/LIMIT в новом (измененном) запросе, а не вытягивать весь набор строк из кеша и манипулировать им с помощью PHP до достичь того же (если между вашим сервером веб-сайтов и серверами базы данных не существует очень высокой задержки).

Попытка управлять проверкой кеша для системы ORM является совершенно другим зверем (из-за отношений) и, вероятно, должна обрабатываться в каждом конкретном случае (в контроллере). Но если вы действительно заинтересованы в производительности, скорее всего, вы не будете использовать ORM для начала.

UPDATE:

Если вы обнаружите, что используете несколько экземпляров одного и того же класса модели в одном потоке, я бы предположил также потенциально memcaching вашей экземплярной модели (в зависимости от вашего конструктора, десериализации и пробуждения объекта иногда более эффективно, чем создание объекта). Когда у вас есть объект с инициализацией (построенный или десериализованный), он миров более эффективен clone() базовый экземпляр объекта и устанавливает его новое состояние, а не восстанавливает объект в PHP.

Ответ 3

Я скептически отношусь к тому, что кеширование обычно не должно выполняться если нет реальной потребности, и не должно быть сделано для таких вещей, как результаты поиска. Так почему-то Модель сам должен знать, Выдается инструкция SELECT достойный кэширования. Не было бы Модель должна быть астрономически умной, и/или хранить статистику того, что чаще всего запрашивается в течение длительного времени период времени, чтобы точно принимать решение? И не накладные расходы на все это делают кэширование бесполезно в любом случае?

Кто еще лучше подходит для отслеживания этого? Несколько контроллеров будут использовать одну и ту же модель для получения требуемых данных. Итак, как в мире контроллер мог бы принять рациональное решение?

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

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

Трудно дать более конкретные советы без контекста, но вот несколько сценариев:

1) У вас есть бизнес-объекты, которые могут иметь назначенную категорию. Категории редко меняются. Модель вашей категории должна кэшировать полный набор категорий для операций чтения. Когда происходят редкие правовые операции, они могут аннулировать кеш. Каждое представление script в системе теперь может запрашивать модель и возвращать текущие категории (например, для рендеринга ящиков выбора), не касаясь себя кешем. Любой контроллер в системе теперь может добавлять/обновлять/удалять категории, не зная о кеше.

2) У вас есть сложная формула, которая потребляет несколько входов и создает рейтинг популярности для каких-то "продуктов". Некоторые виджеты в макете страницы показывают 5 наиболее популярных объектов в сводной форме. Модель вашего продукта предоставит метод getPopular(), который будет опираться на кеш. Модель может аннулировать кеш каждые X минут, или какой-то фоновый процесс может выполняться через регулярные промежутки времени, чтобы аннулировать/перестроить. Независимо от того, какая часть системы хочет популярных продуктов, они запрашивают ее через модель, которая прозрачно управляет кешем.

Точная реализация кэширования сильно зависит от типа данных, которые вы манипулируете, в сочетании с типичными вариантами использования.

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

Это не о моделях, которые умны, а о том, что разработчик умный.

Ответ 4

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

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

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

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

Обновление. Мы также внедрили namespacing внутри нашего кэшера на двух уровнях, для каждой модели и необязательной групповой основе. Благодаря этому мы можем легко аннулировать все ранее недействительные все кэшированные данные, которые поступают от модели при обновлении или удалении в базе данных.

Ответ 5

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

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

У вас будет класс, контролирующий все это. Функции могут включать такие функции, как

-start проверка кеширования -set порог
-cache всегда
время жизни кэша
-force очистить весь кеш
-почти этот кеш для этого запроса
-Мы убили смерть лазером смерти и нужно все поймать (Я ненавижу тебя, Wordpress Я никогда не использую тебя снова, я не должен был быть таким ленивым и делать свою собственную функцию веб-сайта)

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

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

Ответ 6

На самом деле это не ответ, но ваш вопрос напомнил мне, что я видел эту главу, которая описывает, я думаю, как это сделать что вы хотите сделать, используя ORK Doctrine с Symfony. Возможно, вам захочется сравнить с этим подходом/реализацией.

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

Ответ 7

Я бы рекомендовал вам посмотреть здесь для всестороннего изучения кэширования в ORM, включая проблемы и решения, которые могут быть применены.

При работе с кешированием данных в ORM у вас обычно возникают следующие 3 проблемы:

  • Многие реализации ORM сохраняют либо ресурс базы данных, либо несериализуемый результирующий набор, либо оба в реальных объектах ORM. Поскольку кеширование требует, чтобы все объекты были сериализованы, это ставит серьезный дорожный блок на нашем пути.
  • Как вы отслеживаете один набор данных по сравнению с другим в кеше?
  • Как вы уведомляете кеш, который изменил конкретный набор данных?

Ответ 8

У вас должна быть отдельная модель, которая напрямую связана с интерфейсом SQL, например. для таблицы Customers: $CustomerModel->GetCustomers($parameter); et cetera. Затем в этих моделях вы можете реализовать кеширование прозрачно, не редактируя ни один из существующих MVC.