缓存标签

缓存标签(Cache Tag)提供对缓存数据的逻辑分组能力,允许按标签批量清除关联缓存,是实现缓存分类管理的核心功能。

工作原理

每个标签维护一个 Set 集合,存储属于该标签的所有缓存键。清除标签时,遍历集合并删除所有关联缓存。

text
┌─────────────────────────────────────────────────┐
│                  TAG_STORE                       │
│  ┌─────────────┬─────────────┬──────────────┐   │
│  │  tag:users  │  tag:posts  │  tag:products│   │
│  └──────┬──────┴──────┬──────┴──────┬───────┘   │
│         │             │             │            │
│  ┌──────▼──────┐ ┌────▼─────┐ ┌────▼──────┐    │
│  │ user:1      │ │ post:1   │ │ product:1 │    │
│  │ user:2      │ │ post:2   │ │ product:2 │    │
│  │ user:3      │ │ post:3   │ │ product:3 │    │
│  └─────────────┘ └──────────┘ └───────────┘    │
└─────────────────────────────────────────────────┘

基本用法

创建标签并写入缓存

php
use Viswoole\Cache\Facade\Cache;

// 创建单一标签
$userTag = Cache::tag('users');

// 写入缓存并自动关联到标签
$userTag->set('user:1', ['name' => '张三'], 3600);
$userTag->set('user:2', ['name' => '李四'], 3600);
$userTag->set('user:3', ['name' => '王五'], 3600);

多标签关联

php
// 一个缓存可同时关联多个标签
$postTag = Cache::tag(['posts', 'content']);

$postTag->set('post:1', ['title' => '文章标题'], 3600);
// 该缓存同时属于 posts 和 content 标签

手动推送已有缓存到标签

php
// 将已有的缓存键推送到标签
$tag = Cache::tag('articles');
Cache::set('article:100', $data, 3600);
$tag->push('article:100');

清除标签缓存

php
// 清除 users 标签下所有缓存
Cache::tag('users')->clear();

// 这会删除:
// - user:1, user:2, user:3 (缓存数据)
// - tag:users (标签集合)
// - 从 TAG_STORE 中移除 users 标签

查询标签下的缓存键

php
// 获取标签下所有缓存键
$keys = Cache::tag('users')->get();
// 返回: ['user:1', 'user:2', 'user:3']

移除特定缓存与标签的关联

php
$tag = Cache::tag(['users', 'vip']);

// 从标签中移除指定缓存键(同时删除缓存数据)
$tag->remove(['user:2']);

// 如果移除后标签集合为空,标签也会从仓库中删除

标签相关 API

方法说明
tag($name)创建标签实例,支持字符串或数组
set($key, $value, $expire)写入缓存并关联到标签
push($key)将已有缓存键推送到标签
clear()清除标签下所有缓存及标签本身
get()获取标签下所有缓存键列表
remove($keys)从标签移除指定缓存并删除数据
getTags()获取所有已注册的标签列表
getTagKey($tag)获取标签的实际存储键名
getTagStoreName()获取标签仓库名称

典型应用场景

1. 用户资料更新时清除缓存

php
class UserService
{
    public function updateUserProfile(int $userId, array $data): void
    {
        // 更新数据库
        User::where('id', $userId)->update($data);

        // 清除该用户的所有缓存
        Cache::tag("user:{$userId}")->clear();
    }

    public function getUserProfile(int $userId): array
    {
        $cacheKey = "profile:{$userId}";
        $cacheValue = Cache::get($cacheKey);

        if ($cacheValue !== null) {
            return $cacheValue;
        }

        $user = User::find($userId)->toArray();

        // 关联到用户标签
        Cache::tag("user:{$userId}")->set($cacheKey, $user, 3600);

        return $user;
    }
}

2. 分类数据批量刷新

php
class ProductService
{
    public function refreshCategory(int $categoryId): void
    {
        // 更新分类信息后,清除该分类下所有商品缓存
        Cache::tag("category:{$categoryId}")->clear();

        // 预热缓存
        $products = Product::where('category_id', $categoryId)->get();
        $tag = Cache::tag("category:{$categoryId}");

        foreach ($products as $product) {
            $tag->set("product:{$product->id}", $product->toArray(), 7200);
        }
    }
}

3. 多维度标签管理

php
class ArticleService
{
    public function cacheArticle(Article $article): void
    {
        // 文章同时关联多个维度的标签
        $tags = [
            "articles",                    // 全部文章
            "author:{$article->author_id}", // 按作者
            "category:{$article->category_id}", // 按分类
            "status:published",             // 按状态
        ];

        Cache::tag($tags)->set("article:{$article->id}", $article->toArray(), 3600);
    }

    // 按作者清除
    public function clearByAuthor(int $authorId): void
    {
        Cache::tag("author:{$authorId}")->clear();
    }

    // 按分类清除
    public function clearByCategory(int $categoryId): void
    {
        Cache::tag("category:{$categoryId}")->clear();
    }
}

注意事项

  1. 标签不能为空:创建标签时传入空数组会抛出 InvalidArgumentException
  2. 标签名称规范:建议使用有语义的前缀(如 user:category:)避免冲突
  3. 性能考量:大量缓存关联同一标签时,clear() 操作可能较慢
  4. 原子性clear() 操作不是原子的,并发场景下可能出现部分清除
  5. 存储开销:每个标签需要额外的 Set 存储空间来维护键列表