Странности с memcached

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Вот есть запрос(смотрю по дебаг-панели):

Код: Выделить всё

SELECT COUNT(`url`) AS count, `url` FROM `stats` WHERE (`uid` NOT IN (9039386283, 7567680618, 9421802727, 5487722167)) AND (`ip`<>3579284812) GROUP BY `url` ORDER BY `count` DESC LIMIT 60
При первой загрузке занимает ~468мс при второй ~0.3мс

Отсюда делаю вывод, что в мемкеш ничего не упало, по логике вещей при второй и последующих загрузках запроса ведь быть не должно вообще? Вопрос, как такое может быть? Или mysql сам производит какое-то кеширование?

На виртуале все идентично ведет себя!

В контроллере:

Код: Выделить всё

$urlDataProviderQuery = StatsSearch::getDb()->cache(function ($db) {
            return StatsSearch::find()
                ->excluded()
                ->select('COUNT(`url`) AS count, `url`')
                ->groupBy(['url'])
                ->orderBy(['count' => SORT_DESC])
                ->limit(\Yii::$app->config->get('GENERAL.ACTIVE_PER_PAGE'));
        });

$urlDataProvider = new ActiveDataProvider([
            'query' => $urlDataProviderQuery,
            'pagination' => false,
        ]); 
Или я конкретно туплю или что то я совсем уже запутался :)
Yii2!
astronin
Сообщения: 606
Зарегистрирован: 2012.01.30, 17:46

Re: Странности с memcached

Сообщение astronin »

да, mysql тоже что-то кеширует, и следующий тотже запрос база отдаст быстрее
при кешировании в дебагере вообще не должно быть видно тех запросов, которые должны были быть закешированы
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Ну вот, поэтому я делаю вывод что кеширование происходит как то внутри mysql, а в мемкеше фактически ничего нет.
Ну тогда вопрос, а как вапще вы помещаете запросы в кеш? Что все пишут на чистом скл и преобразовывают в массив? И почему он не дает сериализовать объект нормально, в кохане например любое состояние ОРМ можно туда засунуть, а тут.. что то непонятное...
Yii2!
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Эх, жаль, пришлось отказаться от ListView и сделать вывод через foreach.
Зато теперь все залетало, и все запросы в мемкеш упали)
Yii2!
longmayar
Сообщения: 55
Зарегистрирован: 2014.12.08, 16:11

Re: Странности с memcached

Сообщение longmayar »

С каких пор такая конструкция перестала работать?

Код: Выделить всё

        $cache = Yii::$app->cache;
        $stations = $cache->get('main_def_index_stations');

        if ( $stations === false ) {
            $stations = Station::find()
                ->joinWith( ['stationSources'] )
                ->where ( ['is_active' => Station::STATUS_ACTIVE ] )
                ->orderBy ( 'freq' )
                ->all();

            $cache->set('main_def_index_stations', $stations);
        }
Еще месяц назад все было хорошо, теперь: Serialization of 'Closure' is not allowed
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Я решил вопрос иначе, кеш работает теперь на гриды. Но что выше - не сдавалось никак.
Yii2!
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Странности с memcached

Сообщение yiijeka »

Что значит работает на гриды?
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

С датапровайдерами хотел сказать.
Yii2!
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

longmayar писал(а):С каких пор такая конструкция перестала работать?

Код: Выделить всё

        $cache = Yii::$app->cache;
        $stations = $cache->get('main_def_index_stations');

        if ( $stations === false ) {
            $stations = Station::find()
                ->joinWith( ['stationSources'] )
                ->where ( ['is_active' => Station::STATUS_ACTIVE ] )
                ->orderBy ( 'freq' )
                ->all();

            $cache->set('main_def_index_stations', $stations);
        }
Еще месяц назад все было хорошо, теперь: Serialization of 'Closure' is not allowed
Речь была о другой конструкции. Такая работает как и ожидалось.
Yii2!
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Странности с memcached

Сообщение zelenin »

прочел всю ветку, омг.
ДатаПровайдер кэшировать бесполезно - это не запрос в БД. data provide - продоставлять данные. Это класс, облегчающий выборки с пагинациями, лимитами, фильтрами итд. Закэшировав объект провайдера, мы не кэшируем запросы, которые делает с помощью провайдера ГРИД.
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Странности с memcached

Сообщение yiijeka »

кэшируя грид можно споткнуться - viewtopic.php?f=19&t=22944
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Все работает безупречно:

Код: Выделить всё

$uidDataProvider = new ActiveDataProvider([
            'query' => StatsSearch::find()
                ->with(['uidCommentContent'])
                ->excluded()
                ->select(['count' => 'COUNT(`uid`)', 'uid'])
                ->groupBy(['uid'])
                ->orderBy(['count' => SORT_DESC])
                ->limit(\Yii::$app->config->get('GENERAL.ACTIVE_PER_PAGE')),
            'pagination' => false,
        ]);

        \Yii::$app->db->cache(function () use ($uidDataProvider) {
            return $uidDataProvider->prepare();
        });
$uidDataProvider берется из кеша. все пагинаторы и фильтры срабатывают. запросов в дебагере нет при повторном обновлении.
Yii2!
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Странности с memcached

Сообщение zelenin »

Поправка: в первом сообщении даже не провайдер кэшируется, а только Query.

Исследование:

как оно работает: DataProvider - класс, куда мы передаем базовый query (не сам запрос, а только базовый объект ActiveQuery, т.е. User::find() без ->all()). Дальше с помощью пагинации, фильтров, сортировок DataProvider добавляет к query необходимые ->limit()->offset()->andWhere()->orWhere() итд. И перед самым рендерингом данных в методе prepareModels() добавляет ->all() и получает собственно модели (с учетом текущей страницы, сортировок и фльтров). Соответственно, чтобы закэшировать дата провайдер нам надо кэшировать объект провайдера с уже полностью сформированным объектом (страницы, фильтры итд) и сделанным запросом. Понятно, что для первой страницы и второй страницы кэш будет разный. Для первой страницы без фильтров и с фильтрами кэш тоже будет разный. Итд.
Изучаем код:
GridView::renderTableBody()

Код: Выделить всё

$models = array_values($this->dataProvider->getModels());
$keys = $this->dataProvider->getKeys();
Идем в ActiveDataProvider::getModels() (по факту BaseDataProvider::getModels()):

Код: Выделить всё

    public function getModels()
    {
        $this->prepare();

        return $this->_models;
    }
сначала готовит модели (т.е. сам запрос, вносящий модели в переменную _models), потом возвращает

BaseDataProvider::prepare():

Код: Выделить всё

    public function prepare($forcePrepare = false)
    {
        if ($forcePrepare || $this->_models === null) {
            $this->_models = $this->prepareModels();
        }
        if ($forcePrepare || $this->_keys === null) {
            $this->_keys = $this->prepareKeys($this->_models);
        }
    }
как видим, метод готовит модели и ключи, если $_models еще не заполнена.
Проверим ActiveDataProvider::prepareModels()

Код: Выделить всё

    protected function prepareModels()
    {
        if (!$this->query instanceof QueryInterface) {
            throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
        }
        $query = clone $this->query;
        if (($pagination = $this->getPagination()) !== false) {
            $pagination->totalCount = $this->getTotalCount();
            $query->limit($pagination->getLimit())->offset($pagination->getOffset());
        }
        if (($sort = $this->getSort()) !== false) {
            $query->addOrderBy($sort->getOrders());
        }

        return $query->all($this->db);
    }
как видим именно этот метод применяет настройки грида и превращает query в сам запрос.

Делаю вывод: кэшировать DataProvider нужно ТОЛЬКО ПОСЛЕ ПОДГОТОВКИ МОДЕЛЕЙ.
Псевдокод:

Код: Выделить всё

$query = User::find();
$dataProvider = new ActiveDataProvider(['query' => $query]);
$cacheKey = md5(json_encode($dataProvider ));

if (!$dataProviderCache = $cache->get($cacheKey )) {
$dataProvider->prepare();
$cache->set($cacheKey , $dataProvider, 3600);
$dataProviderCache = $dataProvider;
}
Честно говоря, код писал на коленке, поэтому могут быть ошибки, причем критические. Но общий смысл понятен.
longmayar
Сообщения: 55
Зарегистрирован: 2014.12.08, 16:11

Re: Странности с memcached

Сообщение longmayar »

vitalik1183 писал(а):
longmayar писал(а):С каких пор такая конструкция перестала работать?

Код: Выделить всё

        $cache = Yii::$app->cache;
        $stations = $cache->get('main_def_index_stations');

        if ( $stations === false ) {
            $stations = Station::find()
                ->joinWith( ['stationSources'] )
                ->where ( ['is_active' => Station::STATUS_ACTIVE ] )
                ->orderBy ( 'freq' )
                ->all();

            $cache->set('main_def_index_stations', $stations);
        }
Еще месяц назад все было хорошо, теперь: Serialization of 'Closure' is not allowed
Речь была о другой конструкции. Такая работает как и ожидалось.
Так ведь не работает!
longmayar
Сообщения: 55
Зарегистрирован: 2014.12.08, 16:11

Re: Странности с memcached

Сообщение longmayar »

Как вообще с этим работать?

Пытаюсь первый вариант, который работал раньше:

Код: Выделить всё

        $urlPatterns = Yii::$app->cache->get('url-patterns');

        if ( $urlPatterns === false ) {
            $urlPatterns = UrlPattern::find()->orderBy('is_active DESC')->all();
            Yii::$app->cache->set('url-patterns', $urlPatterns);
        }
 
Хрен! Serialization of 'Closure' is not allowed

Пытаюсь второй вариант:

Код: Выделить всё

        $dependency = new DbDependency;
        $dependency->sql = 'SELECT MAX(updated_at) FROM url_pattern';

        $urlPatterns = Yii::$app->getDb()->cache( function( $db ) {
            return UrlPattern::find()->orderBy('is_active DESC')->all();
        }, 10, $dependency);
 
Хрен снова! Оно как запомнилось в кэш - так его и не обновляет. Ни через 10 секунд, ни по изменению максимального updated_at.

Как с этим работать-то? Как насильно почистить кэш для этого запроса?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Странности с memcached

Сообщение zelenin »

longmayar писал(а):
Хрен! Serialization of 'Closure' is not allowed
где-то анонимка используется. перепишите function(...) { ... } на [$class, $methodname]. тогда сериализация пройдет
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Странности с memcached

Сообщение yiijeka »

и папку runtime/cache удалите
sanex3339
Сообщения: 66
Зарегистрирован: 2014.10.16, 20:30

Re: Странности с memcached

Сообщение sanex3339 »

vitalik1183 писал(а):Все работает безупречно:

Код: Выделить всё

$uidDataProvider = new ActiveDataProvider([
            'query' => StatsSearch::find()
                ->with(['uidCommentContent'])
                ->excluded()
                ->select(['count' => 'COUNT(`uid`)', 'uid'])
                ->groupBy(['uid'])
                ->orderBy(['count' => SORT_DESC])
                ->limit(\Yii::$app->config->get('GENERAL.ACTIVE_PER_PAGE')),
            'pagination' => false,
        ]);

        \Yii::$app->db->cache(function () use ($uidDataProvider) {
            return $uidDataProvider->prepare();
        });
 
$uidDataProvider берется из кеша. все пагинаторы и фильтры срабатывают. запросов в дебагере нет при повторном обновлении.
Вот это работает. Спасибо!
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Странности с memcached

Сообщение vitalik1183 »

Так ведь не работает!
Все нормально работает(если данные не в датапровайдер помещаются). Я проверил, есть у меня такое кеширование в проекте, как раз с ->all()
Yii2!
longmayar
Сообщения: 55
Зарегистрирован: 2014.12.08, 16:11

Re: Странности с memcached

Сообщение longmayar »

zelenin писал(а):
longmayar писал(а):
Хрен! Serialization of 'Closure' is not allowed
где-то анонимка используется. перепишите function(...) { ... } на [$class, $methodname]. тогда сериализация пройдет
Так простой же код, как его переписать, чтобы анонимка не использовалась?

Код: Выделить всё

        $urlPatterns = Yii::$app->cache->get('url-patterns');

        if ( $urlPatterns === false ) {
            $urlPatterns = UrlPattern::find()->orderBy('is_active DESC')->all();
            Yii::$app->cache->set('url-patterns', $urlPatterns);
        }
vitalik1183 писал(а):
Так ведь не работает!
Все нормально работает(если данные не в датапровайдер помещаются). Я проверил, есть у меня такое кеширование в проекте, как раз с ->all()
Вот выше код - не работает. Не используется он в датапровайдере.
Ответить