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

Laravel中如何获取带指定前缀的所有缓存键?

解决Laravel中批量获取指定前缀缓存键的问题

针对你在线用户缓存的场景,不想用Tags的话,有两种实用方案可以实现批量获取带前缀的缓存项:

方案一:利用缓存驱动的原生API(推荐Redis场景)

不同缓存驱动原生支持键匹配,直接调用底层方法即可,这里以最常用的Redis为例:

Redis驱动实现

Laravel的Redis缓存可以直接调用Redis客户端的SCAN命令(生产环境优先用这个,比KEYS更安全,不会阻塞Redis)来匹配前缀:

// 获取所有online_users_开头的缓存键
$prefix = 'online_users_';
$keys = [];
$cursor = '0';

do {
    // 调用Redis的SCAN命令,匹配前缀+*
    [$cursor, $matchedKeys] = Cache::store('redis')->connection()->scan($cursor, [
        'match' => $prefix . '*',
        'count' => 1000 // 每次扫描的数量,可根据实际调整
    ]);
    
    $keys = array_merge($keys, $matchedKeys);
} while ($cursor !== '0');

// 批量获取这些键对应的缓存值
$onlineUsers = Cache::many($keys);

如果是开发环境或者缓存规模很小,也可以用简化的KEYS命令(不推荐生产环境):

$keys = Cache::store('redis')->connection()->keys('online_users_*');
$onlineUsers = Cache::many($keys);

Memcached驱动实现

Memcached支持获取所有键,Laravel的Memcached缓存可以通过底层客户端调用:

$memcached = Cache::store('memcached')->getMemcached();
$allKeys = $memcached->getAllKeys();
$matchedKeys = array_filter($allKeys, fn($key) => str_starts_with($key, 'online_users_'));
$onlineUsers = Cache::many($matchedKeys);

注意:部分Memcached环境可能需要开启memcached.statistics配置才能正常获取所有键。

File驱动实现

如果用的是文件缓存,直接扫描缓存目录即可:

$cacheDir = storage_path('framework/cache/data');
$prefix = 'online_users_';
$matchedKeys = [];

// 遍历缓存目录,匹配前缀的键
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($cacheDir)) as $file) {
    if ($file->isFile()) {
        // 解析缓存文件名对应的原始键
        $cacheKey = unserialize(file_get_contents($file->getPathname()));
        if (str_starts_with($cacheKey, $prefix)) {
            $matchedKeys[] = $cacheKey;
        }
    }
}

$onlineUsers = Cache::many($matchedKeys);

方案二:维护缓存键索引(跨驱动通用)

如果需要兼容多种缓存驱动,或者不想依赖驱动原生API,可以维护一个专门的索引缓存,存储所有在线用户的缓存键:

实现步骤

  1. 新增在线用户缓存时,将键加入索引:
$userId = auth()->id();
$cacheKey = "online_users_{$userId}";

// 设置用户在线缓存,5分钟过期
Cache::put($cacheKey, ['user_id' => $userId, 'last_active' => now()], 300);

// 将键加入Redis集合(自动去重,并发安全)
Cache::store('redis')->sadd('online_users_index', $cacheKey);
  1. 中间件更新过期时间时,直接更新用户缓存即可:
// 中间件中更新用户在线缓存的过期时间
$userId = auth()->id();
$cacheKey = "online_users_{$userId}";

if (Cache::has($cacheKey)) {
    Cache::put($cacheKey, Cache::get($cacheKey), 300);
}
  1. 批量获取时,先从索引取所有键,再过滤已过期的:
// 从索引获取所有在线用户缓存键
$allKeys = Cache::store('redis')->smembers('online_users_index');
// 过滤掉已过期的键
$validKeys = array_filter($allKeys, fn($key) => Cache::has($key));
// 批量获取值
$onlineUsers = Cache::many($validKeys);
  1. 定期清理索引中的无效键:
    可以通过Artisan命令定时清理,比如每天执行一次:
// 生成命令:php artisan make:command CleanOnlineUsersIndex
public function handle()
{
    $allKeys = Cache::store('redis')->smembers('online_users_index');
    $invalidKeys = array_filter($allKeys, fn($key) => !Cache::has($key));
    
    if (!empty($invalidKeys)) {
        Cache::store('redis')->srem('online_users_index', ...$invalidKeys);
    }
    
    $this->info('清理完成,移除了'.count($invalidKeys).'个无效缓存键');
}

注意事项

  • Redis的SCAN命令:count参数只是提示Redis每次返回的数量,实际返回可能更多或更少,需要循环直到cursor为0。
  • 生产环境避免用KEYS:KEYS会遍历Redis所有键,缓存规模大时会阻塞服务,SCAN是渐进式遍历,更安全。
  • 索引方案的一致性:如果用户缓存自动过期,索引里的键不会自动删除,所以需要定时清理,或者在用户下线时主动从索引移除键。
  • 驱动兼容性:方案一依赖具体驱动的原生API,方案二是跨驱动通用的,但需要额外维护索引。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:13:25