缓存标签

缓存标签(Cache Tag)为缓存键提供逻辑分组能力:写入时把键挂到标签集合上,之后可按标签批量清除,是「更新即失效」缓存策略的核心工具。

工作原理

每个标签维护一个集合(Set),成员是归属于该标签的缓存键;另有一个标签仓库(Tag Store,键名默认 TAG_STORE)记录所有已注册标签,供 getTags() 全局查询。

text
TAG_STORE(标签仓库)
├── tag:users   →  [user:1, user:2, user:3]
├── tag:posts   →  [post:1, post:2]
└── tag:vip     →  [user:2]

标签键默认加 tag: 前缀以区别于普通缓存键,两者均存储在当前驱动中(可通过驱动的 tag_prefix / tag_store 参数调整,见 缓存驱动)。

基本用法

写入时关联标签

Cache::tag() 接受字符串或字符串数组,返回标签实例;用标签实例的 set() 写入,键会自动追加到所有关联标签的集合中:

php
use Viswoole\Cache\Facade\Cache;

// 单标签
Cache::tag('users')->set('user:1', ['name' => '张三'], 3600);
Cache::tag('users')->set('user:2', ['name' => '李四'], 3600);

// 多标签:该缓存同时归属 posts 与 content 两个分组
Cache::tag(['posts', 'content'])->set('post:1', $article, 3600);

标签实例的 set() 签名与驱动一致:

参数类型默认值说明
keystring必填缓存键
valuemixed必填缓存值
expireDateTime|int|nullnull过期秒数,null 使用驱动默认值
NXboolfalse仅当键不存在时写入

挂载已有缓存

对已通过 Cache::set() 写入的键,可用 push() 补挂到标签上:

php
Cache::set('article:100', $data, 3600);
Cache::tag('articles')->push('article:100');

查询标签下的缓存键

php
// 单标签:返回扁平键列表 ['user:1', 'user:2']
$keys = Cache::tag('users')->get();

// 多标签:返回按标签分组的映射 ['posts' => [...], 'content' => [...]]
$grouped = Cache::tag(['posts', 'content'])->get();

移除指定缓存

remove() 将缓存键从标签集合中剔除并删除缓存数据;若某标签因此变空,该标签也会从标签仓库中移除:

php
Cache::tag('users')->remove('user:1');
Cache::tag(['users', 'vip'])->remove(['user:2', 'user:3']);

清空整组缓存

clear() 删除标签下全部缓存数据、标签集合本身,并从标签仓库中注销该标签:

php
Cache::tag('users')->clear();
// 等价于删除:user:1、user:2、tag:users,并从 TAG_STORE 移除 users

多标签时一次清除所有传入标签:

php
Cache::tag(['posts', 'content'])->clear();

API 速查

方法返回值说明
Cache::tag(string|array $tags)CacheTagInterface创建标签实例,标签为空抛 InvalidArgumentException
->set($key, $value, $expire, $NX)bool写入缓存并自动挂到当前标签
->push($key)void将已有缓存键挂到当前标签并注册标签
->get()array标签下的缓存键列表(多标签返回分组映射)
->remove(string|array $keys)void从标签剔除并删除缓存数据
->clear()void清空标签下全部缓存并注销标签
Cache::getTags()array|false标签仓库中的所有标签,不存在返回 false
Cache::getTagKey(string $tag)string标签的实际存储键(含 tag: 前缀)
Cache::getTagStoreName()string标签仓库键名

典型场景

用户维度缓存失效

php
class UserService
{
  public function getProfile(int $userId): array
  {
    $key = "profile:{$userId}";
    $cached = Cache::get($key);
    if ($cached !== null) {
      return $cached;
    }

    $profile = $this->loadFromDb($userId);
    Cache::tag("user:{$userId}")->set($key, $profile, 3600);
    return $profile;
  }

  public function updateProfile(int $userId, array $data): void
  {
    $this->saveToDb($userId, $data);
    // 用户资料变更后,该用户名下的所有缓存(资料、权限、统计…)一次失效
    Cache::tag("user:{$userId}")->clear();
  }
}

多维度分组

php
// 文章同时挂到 全部文章 / 作者 / 分类 三个维度
Cache::tag(['articles', "author:{$authorId}", "category:{$categoryId}"])
  ->set("article:{$id}", $article, 3600);

// 按任一维度独立失效
Cache::tag("author:{$authorId}")->clear();
Cache::tag("category:{$categoryId}")->clear();

注意事项

  • 标签不能为空Cache::tag([]) 抛出 InvalidArgumentException,标签名请传入非空字符串或非空数组
  • 清除非原子clear() 按集合逐个删除,并发写入同组缓存的瞬间可能残留少量键;对强一致场景建议配合业务锁使用
  • 性能考量:集合成员极多时 clear() 的删除耗时会线性增长,单标签建议控制在合理规模(如按用户、按日期分片)
  • 存储开销:每个标签额外占用一个集合的存储空间
  • 过期残留:键到期被驱动惰性删除后,仍会残留在标签集合中,clear() 时会对已不存在的键做空删除,属正常现象

下一步