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

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

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

(Источник: Сообщение Яррода )

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    C.

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

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

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

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

    ОБНОВИТЬ:

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

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

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

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

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

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

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

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

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

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

    Дело не в том, что Модели умны, а в том, что разработчик умный.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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