Laravel 缓存层次与 Redis
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read260 words

Laravel 缓存层次与 Redis

缓存是提升 Laravel 应用性能最直接的手段。从路由缓存(减少启动开销)到模型数据缓存(避免重复数据库查询),再到分布式 Session,Redis 是连接这些缓存层的核心。


缓存 Driver 对比

Driver 特点 适用场景
file 缓存到文件系统 开发环境、单机部署
redis Redis 内存缓存 生产推荐,分布式、高性能
memcached Memcached 缓存 遗留系统
database 缓存到数据库 无 Redis 时的替代
array 只在当前请求内有效 测试环境
dynamodb AWS DynamoDB 云原生
# .env
CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=null

基础缓存操作

use Illuminate\Support\Facades\Cache;
// 存储(永久)
Cache::forever('site_settings', ['theme' => 'dark']);
// 存储(有效期)
Cache::put('user_stats_1', $stats, now()->addHours(1));
Cache::put('leaderboard', $data, 3600);  // 秒数
// 获取
$stats = Cache::get('user_stats_1');
$stats = Cache::get('user_stats_1', fn() => 'default');  // 缺省值(懒加载)
// 记住(最常用模式:如果有就用缓存,没有就计算并缓存)
$stats = Cache::remember('user_stats_1', 3600, function () {
return Task::where('user_id', 1)->selectRaw('
COUNT(*) as total,
SUM(CASE WHEN status = "done" THEN 1 ELSE 0 END) as completed
')->first();
});
// 永久记住
$settings = Cache::rememberForever('app_settings', fn() => Settings::all());
// 检查
Cache::has('user_stats_1');
Cache::missing('user_stats_1');
// 删除
Cache::forget('user_stats_1');
// 删除多个
Cache::deleteMultiple(['key1', 'key2', 'key3']);
// 清空全部(谨慎!)
Cache::flush();

缓存键命名规范

// 推荐:命名空间:标识符
'tasks:user:42'          // 用户 42 的任务列表
'tasks:stats:user:42'    // 用户 42 的任务统计
'project:1:member_ids'   // 项目 1 的成员 ID 列表
'leaderboard:weekly'     // 每周排行榜
// 使用常量类统一管理
class CacheKeys
{
public static function userStats(int $userId): string
{
return "tasks:stats:user:{$userId}";
}
public static function projectMembers(int $projectId): string
{
return "project:{$projectId}:member_ids";
}
}
// 使用
Cache::remember(CacheKeys::userStats($userId), 3600, fn() => /* ... */);
Cache::forget(CacheKeys::userStats($userId));

原子锁(防止缓存击穿)

use Illuminate\Support\Facades\Cache;
// 问题:多个请求同时发现缓存不存在,并发查询数据库(缓存击穿)
// 解决:原子锁确保只有一个请求重建缓存
$value = Cache::remember('expensive_key', 3600, function () {
// 这里不能防止缓存击穿!多个请求会并发执行
$lock = Cache::lock('expensive_key_lock', 10);  // 10秒锁
if ($lock->get()) {
// 获取了锁:重建缓存
$data = DB::table('...')->get();
$lock->release();
return $data;
} else {
// 没获取锁:等一下再查缓存
sleep(1);
return Cache::get('expensive_key');
}
});
// 更简洁的 block() 方法(等待直到获取锁)
Cache::lock('generate_report')->block(30, function () {
// 最多等待 30 秒,获取锁后执行
GenerateWeeklyReport::dispatch();
});

在模型中使用缓存

// app/Models/User.php
public function getTaskStatsAttribute(): array
{
return Cache::remember(
CacheKeys::userStats($this->id),
now()->addMinutes(30),
fn() => [
'total'     => $this->tasks()->count(),
'pending'   => $this->tasks()->pending()->count(),
'done'      => $this->tasks()->done()->count(),
'overdue'   => $this->tasks()->overdue()->count(),
]
);
}
// 数据变化时清除缓存(在 Observer 中)
// app/Observers/TaskObserver.php
public function saved(Task $task): void
{
Cache::forget(CacheKeys::userStats($task->user_id));
}
public function deleted(Task $task): void
{
Cache::forget(CacheKeys::userStats($task->user_id));
}

Redis 直接操作(超出 Cache 抽象时)

use Illuminate\Support\Facades\Redis;
// 原子递增计数器(如阅读量)
Redis::incr("task:{$taskId}:views");
Redis::incrby("task:{$taskId}:views", 10);
// 有序集合(排行榜)
Redis::zadd('leaderboard', $score, "user:{$userId}");
$top10 = Redis::zrevrange('leaderboard', 0, 9, 'WITHSCORES');
// 列表(简单队列)
Redis::lpush("notifications:{$userId}", json_encode($notification));
$latest = Redis::lrange("notifications:{$userId}", 0, 19);  // 最新20条
// 设置过期时间
Redis::expire("task:{$taskId}:views", 86400);  // 24小时后过期
// Redis 事务(批量操作保证原子性)
Redis::pipeline(function ($pipe) use ($userId) {
$pipe->incr("user:{$userId}:task_count");
$pipe->incr("daily:task_count:" . date('Y-m-d'));
$pipe->expire("user:{$userId}:task_count", 86400);
});

下一节路由缓存、配置缓存、视图缓存——除了数据缓存,Laravel 还有框架级别的缓存命令:route:cacheconfig:cacheview:cache——生产部署必须执行这些命令。