Здравствуйте,
В проекте запустили кеш на redis'е на запрашиваемые из бд данные, и вслед за ним встала закономерная задача с его обновлением.
Очевидно, что надо кеш обновлять при изменении данных, вопрос в том, как правильно и в каком месте где это делать?
Есть есть ряд ситуаций на которые и возникают вопросы:
* afterSave единичной модели – а что если мы обновляем данные большим скопом и тогда проще
* данные обновляются через updateAttributes()
* запросы напрямую к БД без AR
- как не забыть и учесть про все эти обновления?
Если ранее пришли к практике использования репозитория для получения данных - туда же и вставили кеширование данные, то правильно ли будет использовать те же репозитории для сохранения данных, или есть другие подходы?
Опять таки где-то есть обработки, где-то есть другой код который имеет место к этим данным - как не пропустить все это и не забыть?
Обновление кеша
Re: Обновление кеша
Только закончил покрытие проекта кешем
Сразу скажу , если проект большой то вероятность того что где то кеш забудешь обновить - очень большая
Тут надо аккуратно и ответ на вопрос :
>как не пропустить все это и не забыть?
или записывать где то или помнить
я использую вот такое поведение которое прикручиваю к нужной модели кеш которой нужно обновлять + обновяль кеши которые связанные :
код доработал что бы можно было фрагмент страницы удалять из кеша так как он не как простой ключ хранится
вот так использую если надо обновить кеш :
вот так кеширую :
и повторю
все нужно делать крайне осторожно и последовательно ибо легко потеряться в погоне кешировать всего и побольше
Сразу скажу , если проект большой то вероятность того что где то кеш забудешь обновить - очень большая
Тут надо аккуратно и ответ на вопрос :
>как не пропустить все это и не забыть?
или записывать где то или помнить
я использую вот такое поведение которое прикручиваю к нужной модели кеш которой нужно обновлять + обновяль кеши которые связанные :
Код: Выделить всё
<?php
/**
* Eugine Terentev <[email protected]>
*/
namespace common\behaviors;
use Yii;
use yii\base\Behavior;
use yii\caching\TagDependency;
use yii\db\ActiveRecord;
/**
* CacheInvalidateBehavior automatically invalidates cache by specified keys or tags
* public function behaviors()
* {
* return [
* [
* 'class' => CacheInvalidateBehavior::class,
* 'tags' => [
* 'awesomeTag',
* function($model){
* return "tag-{$model->id}";
* }
* ],
* 'keys' => [
* 'awesomeKey',
* function($model){
* return "key-{$model->id}";
* }
* ],
* 'fragments' => [
* 'awesomeKey',
* function($model){
* return "key-{$model->id}";
* }
* ]
* ],
* ];
* }
* ```
* @package common\behaviors
*/
class CacheInvalidateBehavior extends Behavior
{
/**
* @var string Name of cache componentj
*/
public $cacheComponent = 'cache';
/**
* @var array List of tags to invalidate
*/
public $tags = [];
/**
* @var array List of keys to invalidate
*/
public $keys = [];
/**
* @var array List of pages fragments to invalidate
*/
public $fragments = [];
/**
* @var
*/
private $cache;
/**
* Get events list.
* @return array
*/
public function events()
{
return [
ActiveRecord::EVENT_AFTER_DELETE => 'invalidateCache',
ActiveRecord::EVENT_AFTER_INSERT => 'invalidateCache',
ActiveRecord::EVENT_AFTER_UPDATE => 'invalidateCache',
];
}
/**
* Invalidate cache connected to model.
* @return bool
*/
public function invalidateCache()
{
if (!empty($this->keys)) {
$this->invalidateKeys();
}
if (!empty($this->tags)) {
$this->invalidateTags();
}
if (!empty($this->fragments)) {
$this->invalidateFragments();
}
return true;
}
/**
* Invalidates
*/
protected function invalidateKeys()
{
foreach ($this->keys as $key) {
if (is_callable($key)) {
$key = call_user_func($key, $this->owner);
}
$this->getCache()->delete($key);
}
}
/**
* @return \yii\caching\Cache
*/
protected function getCache()
{
return $this->cache ?: Yii::$app->{$this->cacheComponent};
}
/**
*
*/
protected function invalidateTags()
{
TagDependency::invalidate(
$this->getCache(),
array_map(function ($tag) {
if (is_callable($tag)) {
$tag = call_user_func($tag, $this->owner);
}
return $tag;
}, $this->tags)
);
}
/**
* Invalidate pages fragments
*/
protected function invalidateFragments()
{
foreach ($this->fragments as $key) {
if (is_callable($key)) {
$key = call_user_func($key, $this->owner);
}
$buldKey = ['yii\widgets\FragmentCache', $key];
$this->getCache()->delete($buldKey);
}
}
}
вот так использую если надо обновить кеш :
Код: Выделить всё
class ForumTopic extends \yii\db\ActiveRecord
{
...
/**
* @inheritdoc
*/
public static function tableName()
{
return 'forum_topic';
}
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'blameable' => [
'class' => BlameableBehavior::className(),
],
[
'class' => SluggableBehavior::className(),
'attribute' => 'name'
],
[
'class' => TimestampBehavior::className(),
'updatedAtAttribute' => false,
'value' => Time::getNowTime(),
],
[
'class' => CacheInvalidateBehavior::class,
'tags' => [
'table.forum',
'table.forum_topic'
// function ($model,$test) {
// return "table.forum_topic-{$model->id}";
// }
],
'fragments' => [
'index_page.new-topics-block'
]
],
];
...
вот так кеширую :
Код: Выделить всё
class ForumMessage extends \yii\db\ActiveRecord
{
...
/**
* @return \yii\db\ActiveQuery
*/
public function getTopic()
{
return $this->hasOne(ForumTopic::className(), ['id' => 'topic_id'])
->cache(2 * Time::SECONDS_IN_A_DAY, new TagDependency(['tags' => 'table.forum_topic']));
}
...
все нужно делать крайне осторожно и последовательно ибо легко потеряться в погоне кешировать всего и побольше
Re: Обновление кеша
А !
Забыл ответить на
>данные обновляются через updateAttributes()
ответ только один - переписывать на $model->update(false)
я после такого опыта с кешированием очень сильно пожалел что везде использовал этот сахарок
>запросы напрямую к БД без AR
это не важно
Забыл ответить на
>данные обновляются через updateAttributes()
ответ только один - переписывать на $model->update(false)
я после такого опыта с кешированием очень сильно пожалел что везде использовал этот сахарок
>запросы напрямую к БД без AR
это не важно
Код: Выделить всё
$result = $db->cache(function ($db) {
// ... выполнять SQL запросы здесь ...
return $result;
}, $duration, $dependency);