CakePHP 3.5.13
В методе контроллера я кэширую запрос к базе данных следующим образом:
$substances = TableRegistry::get('Substances');
$query = $substances->find()->limit($limit)->offset($offset);
$query->cache(function ($query) {
return 'substance_results_' . md5(serialize($query->sql()));
});
$this->set('data', $query->all());
Это создает кешированный файл (мы используем Redis для кеширования в этом приложении), содержащий результаты запроса. Например, в Redis я вижу следующее:
127.0.0.1:6379> keys *
3) "cake_redis_substance_results_cb799f6526c148d133ad9ce9245b23be"
4) "cake_redis_substance_results_dbc7b0b99dff3ab6a20cbdfbbd09be8c"
Если тот же запрос ($query) будет выполнен еще раз, Cake прочитает содержимое соответствующего кэшированного файла. Как это возможно, учитывая, что мы не сообщаем ему, с какого ключа читать кэшированные данные? Мы сообщаем ему имя ключа для написать в, но не читать из.
Чтобы проиллюстрировать это, скажем, я сделал:
$query->cache(function ($query) {
return 'foo_' . md5(serialize(time()));
});
Здесь я создал совершенно другой ключ, не основанный на выполняемом SQL. Никакая часть кода не сообщает ему, какой ключ соответствует какому запросу.
В документации (https://book.cakephp.org/3.0/en/orm/query-builder.html#caching-loaded-results) по кэшированию загруженных результатов это не объясняется в отношении ORM. Он говорит:
The cache method makes it simple to add cached results to your custom finders or through event listeners.
When the results for a cached query are fetched...
Он сообщает вам, как писать (добавлять) в кеш, но не читать (извлекать) из него по отношению к ORM.
Я читал разделы документации, в которых рассказывается, как читать из кеша обычным, не связанным с ORM способом (с использованием Cache::read($key)), но это совершенно не связано с автоматическим выполнением ORM. В случае Cache::read($key) вам необходимо предоставить ключ ($key), который сообщает ему, из какого файла кеша читать данные - я могу это понять, поскольку вы явно указываете ему, какой ключ читать. Но это не относится к ORM и объектам запроса.
Пожалуйста, кто-нибудь может это прояснить?





В CookBook, скорее всего, не упоминаются дополнительные требования, потому что их нет.
Ключ кеша для пишу, конечно, должен быть таким же, как ключ кеша для чтение, все остальное не будет иметь никакого смысла, т.е. запрос будет использовать все, что вы передаете методу QueryTrait::cache() как для чтения, так и для записи, что означает, что вы находятся явно определяет ключ для чтения прямо здесь, в закрытии, которое вы передаете.
Проверьте описание метода, он говорит примерно то же самое:
/**
* Enable result caching for this query.
*
* If a query has caching enabled, it will do the following when executed:
*
* - Check the cache for $key. If there are results no SQL will be executed.
* Instead the cached results will be returned.
* - When the cached data is stale/missing the result set will be cached as the query
* is executed.
*
* ### Usage
*
* ```
* // Simple string key + config
* $query->cache('my_key', 'db_results');
*
* // Function to generate key.
* $query->cache(function ($q) {
* $key = serialize($q->clause('select'));
* $key .= serialize($q->clause('where'));
* return md5($key);
* });
*
* [...]
*
* @param false|string|\Closure $key Either the cache key or a function to generate the
* cache key. When using a function, this query instance will be supplied as an argument.
*
* [...]
*/
Каждый раз, когда выполняется запрос, он проверяет, передали ли вы ключ кеша, а также оценивает и использует его для чтения и записи кешированных результатов соответственно. Следовательно, вы должны убедиться, что ключ кеша является «статическим», чтобы вся вещь кеширования запросов могла быть полезной.
Вы можете использовать замыкание для динамического построения ключа, но результат должен быть статическим, т.е. для одного и того же запроса он должен генерировать один и тот же ключ каждый раз, когда он вызывается. Есть причина, по которой метод QueryTrait::cache() принимает не только замыкание, но и строку!
Я уже упоминал об этом раньше, это происходит в \Cake\Datasource\QueryTrait::all() и \Cake\Datasource\QueryCacher::fetch(), взгляните на источник, чтобы лучше понять, как это работает.
Спасибо за разъяснения. Я действительно считаю, что это следует упомянуть в документации. По сути, ответ заключается в том, что в первом наборе кода, который я опубликовал,
serialize($query->sql())имеет дело как с записью, так и с чтением из кеша, потому что$query->sql()- это строка SQL, которая должна быть выполнена, и поэтому изменяется только при изменении самого запроса. Это означает, что он может найти (прочитать) кэшированный ключ, если один и тот же SQL запускается более одного раза, потому что соответствующий ключ будет существовать.