CakePHP 3.x ORM 如何在不显式定义的情况下知道要读取哪个缓存文件?

How does CakePHP 3.x ORM know which cache file to read from without explicitly defining it?

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 很可能没有提到额外的要求,因为没有。

的缓存key当然要和的缓存key一样,其他都不行从某种意义上说,即查询将使用您传递给 QueryTrait::cache() 方法的任何内容来进行读取和写入,这意味着您 明确定义读取键,就在其中您正在传递的闭包。

检查 the method description,它几乎说了同样的事情:

/**
 * 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.
 *
 * [...]
 */

每次执行查询时,它都会检查您是否传递了缓存键,并评估并使用它来相应地读取和写入缓存结果。因此,您必须确保缓存键是 "static" 才能使整个查询缓存有用。

您可以使用闭包来动态构建密钥,但结果必须是静态的,即对于相同的查询,它必须在每次被调用时生成相同的密钥。 QueryTrait::cache() 方法不仅接受闭包,而且接受字符串,这是有原因的!

我之前提到过,这发生在 \Cake\Datasource\QueryTrait::all() and \Cake\Datasource\QueryCacher::fetch(),请查看源代码以更好地了解它的工作原理。