首页 > 解决方案 > CakePHP 3.x ORM 如何在不明确定义的情况下知道要读取哪个缓存文件?

问题描述

CakePHP 3.5.13

在 Controller 方法中,我正在缓存数据库查询,如下所示:

$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 解释这一点。它说:

缓存方法使缓存结果添加到自定义查找器或通过事件侦听器变得简单。

当获取缓存查询的结果时...

它告诉您如何写入(添加)到缓存中,但不从 ORM 中读取(获取)。

我已经阅读了文档的部分内容,这些部分告诉您如何以一般的、非 ORM 特定的方式(使用Cache::read($key))从缓存中读取数据,但这与让 ORM 自动执行完全不同。如果Cache::read($key)您必须提供一个键 ( $key) 来告诉它从哪个缓存文件中读取数据 - 我可以理解这一点,因为您明确告诉它要读取哪个键。但 ORM 和查询对象并非如此。

请问有人可以澄清一下吗?

标签: cachingcakephpormcakephp-3.0query-builder

解决方案


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()中,请查看源代码以更好地了解它的工作原理。


推荐阅读