ThinkPHP模型的cache()方法仅设置缓存键名,不支持缓存标签;真正按标签管理需手动使用Cache::tag()配合序列化结果存储与读取。

ThinkPHP 的模型查询缓存本身不支持原生「缓存标签(tag)」机制,cache() 方法传入的字符串参数(如 'user_list')只是缓存键名,不是可批量管理的标签。真要用标签做按组清除,得绕过模型默认缓存,手动对接底层缓存驱动。
为什么 cache('tag_name') 不是缓存标签
很多人在模型里写 UserModel::where('status', 1)->cache('user')->select(),以为 'user' 是标签,其实它只是生成缓存键的一部分(最终键可能是 think:UserModel:xxx),底层并未调用缓存驱动的 tag() 方法。所以用 Cache::clear('user') 或 Cache::tag('user')->clear() 都无效。
-
cache('user')→ 仅设置缓存键前缀,不启用标签功能 - ThinkPHP 6.x 默认缓存驱动(如 File、Redis)中,
tag()是独立接口,模型查询不自动绑定 - 只有显式调用
Cache::tag('user')->set($key, $value)才真正打上标签
如何让模型查询走带标签的缓存
必须跳过模型的 cache(),改用缓存门面 + 手动序列化查询结果。核心思路:先构造唯一缓存键,再用 Cache::tag() 存储。
- 用
md5(json_encode($query->getOptions()))或业务规则生成稳定键名,比如'user_list_status_1' - 查库前先
Cache::tag('user')->get($key),命中则直接返回 - 未命中则执行
$query->select(),再Cache::tag('user')->set($key, $result, 3600) - 注意:
select()返回的是Collection,需用$result->toArray()再缓存,否则反序列化可能失败
示例:
立即学习“PHP免费学习笔记(深入)”;
$key = 'user_list_status_1';
$result = Cache::tag('user')->get($key);
if (is_null($result)) {
$result = UserModel::where('status', 1)->select()->toArray();
Cache::tag('user')->set($key, $result, 3600);
}
按标签清除缓存的正确姿势
只有用 Cache::tag('xxx') 存过的数据,才能用对应标签清除。模型自带 cache(true) 或 cache('key') 的数据完全不受影响。
- 清除全部用户相关缓存:
Cache::tag('user')->clear() - 清除 Redis 下所有带
user标签的项(File 驱动也支持) - 不能混用:用
Cache::store('redis')->tag('user')存的,就不能用Cache::store('file')->tag('user')清 - 多级缓存场景下,标签只作用于当前 store 实例,跨驱动不共享
容易被忽略的兼容性细节
标签功能在 ThinkPHP 6.0+ 中依赖缓存驱动是否实现 TagSetInterface。File、Redis、Memcached 驱动支持,但 Apcu、Xcache 等不支持标签,调用 tag() 会静默退化为普通缓存。
- 检查驱动是否支持:
Cache::tag('test')->set('x', 'y')后再Cache::tag('test')->clear(),确认是否真清掉 - Redis 驱动下,标签实际通过
zSet维护索引,若 Redis 内存满或 key 过期策略激进,可能导致标签索引丢失 - 模型事件(如
afterWrite)里清除缓存时,务必用和写入时相同的store和tag实例
真要靠标签做缓存治理,就得放弃模型的快捷 cache(),全程手管键名、标签、序列化逻辑——省事和可控,得选一个。


















