think-orm 2.0.62 单独设置数据表字段缓存驱动和数据缓存驱动
think-cache 拥有强大的多通道(Multi-store)管理能力,但问题的根源在于 think-orm 底层的调用机制太死板。即使您在 think-cache 中配置了 file 和 redis 两个完全独立的通道,think-orm 默认也只会向您通过 Db::setCache() 注入的那个“主通道”发送所有缓存请求。
要真正实现数据缓存(Data Cache)与数据表字段缓存(Schema/Fields Cache)的驱动分离管理,有以下两种最佳实践方案:
方案一:配合 think-cache 自定义代理驱动(最彻底、支持常驻内存)
这是利用 think-cache 解决该问题最标准的方式。我们可以自定义一个代理类,它同样实现 Psr\SimpleCache\CacheInterface 规范。当 think-orm 调用它时,它会根据缓存键名(Key)的特征,自动将请求分流到 think-cache 的不同通道。
Step 1: 创建缓存分流代理类
在项目中新建一个代理类,例如 app\common\OrmCacheProxy.php:
namespace app\common;
use Psr\SimpleCache\CacheInterface;
use think\facade\Cache;
class OrmCacheProxy implements CacheInterface
{
// 获取实际的 think-cache 驱动通道
protected function getDriver(string $key)
{
// think-orm 2.0 字段缓存的 key 默认带有数据库名或表名标识(如 schema:xxx 或特定 md5 格式)
// 如果想把字段缓存存文件(file),常规数据缓存存Redis
if (str_contains($key, 'schema') || str_contains($key, 'fields')) {
return Cache::store('file'); // 切换到 think-cache 的 file 通道
}
return Cache::store('redis'); // 默认数据缓存走 redis 通道
}
public function get($key, $default = null) {
return $this->getDriver($key)->get($key, $default);
}
public function set($key, $value, $ttl = null) {
return $this->getDriver($key)->set($key, $value, $ttl);
}
public function delete($key) {
return $this->getDriver($key)->delete($key);
}
public function clear() {
// 清理时需要同时清理两边
Cache::store('file')->clear();
return Cache::store('redis')->clear();
}
public function getMultiple($keys, $default = null) {
// 简单处理,直接循环
$result = [];
foreach ($keys as $key) { $result[$key] = $this->get($key, $default); }
return $result;
}
public function setMultiple($values, $ttl = null) {
foreach ($values as $key => $value) { $this->set($key, $value, $ttl); }
return true;
}
public function deleteMultiple($keys) {
foreach ($keys as $key) { $this->delete($key); }
return true;
}
public function has($key) {
return $this->getDriver($key)->has($key);
}
}
Step 2: 在全局初始化时注入此代理
在应用启动或中间件中(如 Webman 的 bootstrap.php 或 ThinkPHP 的 service.php),将这个代理实例注入给 Db:
use think\facade\Db;
use app\common\OrmCacheProxy;
// 注入自定义的分流代理
Db::setCache(new OrmCacheProxy());
方案二:利用底层扩展参数,强制隔离(最原生、无代码污染)
如果您只想把数据表字段缓存存在本地文件,而数据缓存(->cache())存在 Redis,其实可以绕过 Db::setCache() 的限制。
think-orm 内置了一个完全独立于 Db::setCache() 的文件字段缓存路径参数:schema_cache_path。
在您的 database.php 或 think-orm.php 配置文件中,直接进行如下配置:
return [
// ... 其它连接配置
'fields_cache' => true, // 开启字段缓存
// 关键点:直接通过目录指定,ORM 底层会强制使用原生 File 写入该目录,不走全局 Cache 驱动
'schema_cache_path' => runtime_path() . 'schema' . DIRECTORY_SEPARATOR,
];
接着,您只需要把全局的 Db::setCache() 设置为您需要的 Redis 驱动:
use think\facade\Db;
use think\facade\Cache;
// 全局只管数据查询缓存,全部注入为 Redis 通道
Db::setCache(Cache::store('redis'));
为什么这个方案能生效?
当 schema_cache_path 被配置了有效路径时,think-orm 底层的 Schema 类在读取表结构时,会优先使用内部的物理文件读写逻辑,从而和通过 Db::setCache() 传入的查询数据缓存驱动完全剥离开。
请问您目前使用的是方案二这种“字段存本地文件、数据存 Redis”的典型场景吗?如果仍需要更复杂的隔离(比如字段存 Memcached,数据存 Redis),我们可以继续完善方案一的代理规则。