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的键空间通知,监听缓存键的过期事件,实时从引用集合中移除对应条目:
- 修改Redis配置(
redis.conf或Redis Enterprise控制台),开启键空间通知:
notify-keyspace-events Ex
- 在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
相关产品推荐
相关产品推荐

