You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 9 Redis缓存标签管理的standard_ref/forever_ref集合过大求助

解决Laravel 9 Redis标签缓存standard_ref/forever_ref集合膨胀问题

不用放弃缓存标签,以下是几个可行的解决方法:

1. 定时清理过期引用的Artisan命令

Laravel不会自动清理标签引用集合里的失效键,写一个自定义Artisan命令,定期扫描并移除集合中对应缓存键已过期的引用:

创建命令文件 app/Console/Commands/CleanCacheTagReferences.php:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class CleanCacheTagReferences extends Command
{
    protected $signature = 'cache:clean-tag-references';
    protected $description = '清理Laravel缓存标签引用集合中的过期键';

    public function handle()
    {
        $referenceSets = ['standard_ref', 'forever_ref'];

        foreach ($referenceSets as $set) {
            $this->info("开始处理集合: {$set}");
            $cursor = 0;
            $removedCount = 0;

            do {
                // 使用SSCAN遍历集合,避免KEYS命令阻塞Redis
                [$cursor, $members] = Redis::command('SSCAN', [$set, $cursor]);

                foreach ($members as $member) {
                    // Laravel的标签引用格式为 "tag:{标签哈希}:{缓存键}",拆分出缓存键
                    list(, $cacheKey) = explode(':', $member, 2);
                    
                    // 检查缓存键是否存在(已过期的键会被Redis自动删除,所以EXISTS返回0)
                    if (!Redis::exists($cacheKey)) {
                        Redis::srem($set, $member);
                        $removedCount++;
                    }
                }

            } while ($cursor !== 0);

            $this->info("从{$set}中移除了{$removedCount}条过期引用");
        }

        $this->info('缓存标签引用清理完成');
    }
}

然后在app/Console/Kernel.php的$commands数组中注册该命令,再加到调度里定期执行:

protected function schedule(Schedule $schedule)
{
    // 每天凌晨2点执行清理
    $schedule->command('cache:clean-tag-references')->dailyAt('02:00');
}

2. 利用Redis键空间通知实时清理

开启Redis的键空间通知,监听缓存键的过期事件,实时从引用集合中移除对应条目:

  1. 修改Redis配置(redis.conf或Redis Enterprise控制台),开启键空间通知:
notify-keyspace-events Ex
  1. 在Laravel中编写监听逻辑,比如在App\Providers\EventServiceProvider的boot方法中:
use Illuminate\Support\Facades\Redis;

public function boot()
{
    parent::boot();

    // 订阅db0的键过期事件(根据你的Redis数据库编号调整)
    Redis::subscribe(['__keyevent@0__:expired'], function ($expiredKey) {
        $cachePrefix = config('cache.prefix');
        // 只处理Laravel缓存键
        if (str_starts_with($expiredKey, "{$cachePrefix}:")) {
            $this->removeTagReferencesForExpiredKey($expiredKey);
        }
    });
}

private function removeTagReferencesForExpiredKey(string $expiredKey)
{
    // 假设你在设置缓存时,将缓存键对应的标签引用存储在一个哈希表中
    $referenceEntries = Redis::hgetall("cache_tag_refs:{$expiredKey}");
    foreach ($referenceEntries as $set => $member) {
        Redis::srem($set, $member);
    }
    Redis::del("cache_tag_refs:{$expiredKey}");
}

这种方式需要在自定义缓存驱动中修改标签设置逻辑,记录缓存键与引用条目的映射,实现实时清理。

3. 调整缓存策略优化

  • 尽量减少forever缓存标签的使用:永久缓存的引用会一直留在forever_ref中,除非手动清理,能改用带TTL的缓存就不用永久缓存。
  • 拆分大标签:避免单个标签关联过多缓存键,拆分多个细分标签,降低单个引用集合的大小。

4. 升级Laravel版本(可选)

Laravel 10+对缓存标签的内部处理有优化,可能减少无效引用的积累,若业务允许,可考虑升级到较新的稳定版本。

内容的提问来源于stack exchange,提问作者zanderwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:20:29