MeWrite Docs

Laravel Redis Cache Tags flush() not working

LaravelでRedisキャッシュタグのflush()が正常に動作しない問題

概要

LaravelでRedisキャッシュドライバを使用している際、Cache::tags(['tag'])->flush()が正常に動作せず、タグ付きキャッシュが削除されない問題です。特にRedisクラスター環境で発生しやすい問題です。

エラーメッセージ

Cache items not being flushed when using tags
RedisException: CROSSSLOT Keys in request don't hash to the same slot
Cache::tags()->flush() returns true but items still exist

原因

  1. Redisクラスターの制限: クラスター環境でのタグ操作の制限
  2. キープレフィックスの問題: 異なるプレフィックスでキャッシュが保存されている
  3. シリアライズの不整合: put()とremember()で異なる形式
  4. タグの管理方法: Laravelのタグ実装の仕様
  5. Redis接続の問題: 異なる接続を使用している

解決策

1. 基本的なタグキャッシュの使用方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use Illuminate\Support\Facades\Cache;

// タグ付きキャッシュの保存
Cache::tags(['users', 'profiles'])->put('user:1', $user, 3600);

// タグ付きキャッシュの取得
$user = Cache::tags(['users', 'profiles'])->get('user:1');

// 特定タグのキャッシュをクリア
Cache::tags(['users'])->flush();

// 複数タグのキャッシュをクリア
Cache::tags(['users', 'profiles'])->flush();

2. Redisクラスター対応の設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// config/database.php
'redis' => [
    'client' => env('REDIS_CLIENT', 'phpredis'),

    'clusters' => [
        'default' => [
            [
                'host' => env('REDIS_HOST', '127.0.0.1'),
                'password' => env('REDIS_PASSWORD'),
                'port' => env('REDIS_PORT', 6379),
                'database' => env('REDIS_CACHE_DB', 1),
            ],
        ],
        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
        ],
    ],
],

3. キャッシュプレフィックスの統一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// config/cache.php
'redis' => [
    'driver' => 'redis',
    'connection' => 'cache',
    'lock_connection' => 'default',
    'prefix' => env('CACHE_PREFIX', 'laravel_cache'),  // 統一
],

// .env
CACHE_PREFIX=myapp_cache

4. タグキャッシュをクリアするカスタムコマンド

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// app/Console/Commands/FlushTaggedCache.php
namespace App\Console\Commands;

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

class FlushTaggedCache extends Command
{
    protected $signature = 'cache:flush-tag {tag}';
    protected $description = 'Flush cache by tag (Redis compatible)';

    public function handle(): void
    {
        $tag = $this->argument('tag');
        $prefix = config('cache.prefix');

        // タグに関連するキーを取得
        $tagKey = "{$prefix}:tag:{$tag}:entries";
        $entries = Redis::smembers($tagKey);

        // 関連するキーを削除
        foreach ($entries as $entry) {
            Redis::del($entry);
        }

        // タグ自体を削除
        Redis::del($tagKey);

        $this->info("Flushed {$tag} tag with " . count($entries) . " entries");
    }
}

5. PhpRedis拡張の設定確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// config/database.php
'redis' => [
    'client' => 'phpredis',  // predisではなくphpredisを使用

    'default' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_DB', 0),
        'read_timeout' => 60,
    ],

    'cache' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_CACHE_DB', 1),  // 別のDBを使用
    ],
],

6. タグなしでの代替実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// タグの代わりにキープレフィックスを使用
class CacheService
{
    public function putUserCache(int $userId, $data): void
    {
        $key = "users:{$userId}:profile";
        Cache::put($key, $data, 3600);
    }

    public function flushUserCaches(): void
    {
        $prefix = config('cache.prefix');
        $keys = Redis::keys("{$prefix}:users:*");

        foreach ($keys as $key) {
            // プレフィックスを除去してから削除
            $shortKey = str_replace("{$prefix}:", '', $key);
            Cache::forget($shortKey);
        }
    }
}

7. remember()使用時の注意

1
2
3
4
5
6
7
// remember()でもタグを使用
$user = Cache::tags(['users'])->remember('user:1', 3600, function () {
    return User::find(1);
});

// タグなしでremember()を使うとflush()で削除されない
// NG: Cache::remember('user:1', 3600, fn() => User::find(1));

8. Redis Clusterでのタグ使用(ハッシュタグ)

1
2
3
4
// Redisクラスターではハッシュタグを使用
// 同じスロットに配置されるようにする
Cache::tags(['{users}'])->put('{users}:1', $user, 3600);
Cache::tags(['{users}'])->flush();

9. イベントリスナーでのキャッシュクリア

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// app/Listeners/ClearUserCache.php
namespace App\Listeners;

use App\Events\UserUpdated;
use Illuminate\Support\Facades\Cache;

class ClearUserCache
{
    public function handle(UserUpdated $event): void
    {
        // タグでクリア
        Cache::tags(['users'])->flush();

        // または特定キーをクリア
        Cache::forget("user:{$event->user->id}");
    }
}

10. デバッグ用のキャッシュ確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Redisに保存されているキーを確認
Route::get('/debug-cache', function () {
    $prefix = config('cache.prefix');

    // すべてのキーを取得
    $keys = Redis::keys("{$prefix}:*");

    // タグ関連のキーを取得
    $tagKeys = Redis::keys("{$prefix}:tag:*");

    return [
        'total_keys' => count($keys),
        'tag_keys' => $tagKeys,
        'sample_keys' => array_slice($keys, 0, 20),
    ];
});

11. Atomic Locksの使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// キャッシュクリア時の競合を防ぐ
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('flush-users-cache', 10);

if ($lock->get()) {
    try {
        Cache::tags(['users'])->flush();
    } finally {
        $lock->release();
    }
}

よくある間違い

  • Redisクラスター環境でタグを使おうとする(制限あり)
  • タグ付きとタグなしのキャッシュを混在させる
  • Cache::flush()Cache::tags()->flush()を混同する
  • 異なるキャッシュ接続でタグ操作を行う

関連エラー

参考リンク

Laravel の他のエラー

最終更新: 2025-12-14