缓存不是「加个 Cache::set 就完事」。键怎么命名、什么时候失效、Redis 挂了业务还能不能跑,这些决定缓存是帮忙还是添乱。

配置 Redis 连接
// config/cache.php
return [
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'redis',
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'password' => env('REDIS_PASSWORD', ''),
'select' => 0,
'prefix' => 'app:',
'expire' => 3600,
'persistent' => true,
],
],
];
prefix 很重要:同一台 Redis 往往被多个项目共用,没有前缀很容易与别的业务撞键。多环境共用时再加一层环境名,例如 app:prod:。
常用 API 与 remember
use think\facade\Cache;
// 手动读写
Cache::set('post:hot', $list, 600);
$list = Cache::get('post:hot', []);
// 读不到时执行闭包并写入,最常用的写法
$list = Cache::remember('post:hot', function () {
return Post::where('status', 'published')
->order('views', 'desc')
->limit(10)
->select()
->toArray();
}, 600);
remember 把「查询 + 写入」合成一步,减少忘记写缓存的概率。删除时用 Cache::delete($key),不要用 set($key, null) —— 后者会让 get 返回 null,与「不存在」难以区分。
键命名规范
建议统一成「模块:对象:标识:维度」的形式,例如:
post:detail:1024:文章详情;post:list:category-3:page-2:分类列表第 2 页;user:profile:88:用户资料;stat:visit:2026-10-21:按天统计。
好处是能用 keys 或扫描定位同模块的键,也方便在后台做「清空某模块缓存」。注意生产环境不要频繁使用 keys *,它会阻塞 Redis,改用固定前缀 + 维护一份键清单。
穿透、击穿与雪崩
- 穿透:查询不存在的数据,缓存永远不命中。做法是缓存空值(短过期)或在入口做参数校验;
- 击穿:热点键过期瞬间大量请求打到数据库。做法是加互斥锁(
Cache::store('redis')->handler()用setnx)或逻辑过期; - 雪崩:大批键在同一时刻过期。做法是过期时间加随机抖动,例如 600 秒 + 0~120 秒随机。
Session 放 Redis
// config/session.php
return [
'type' => 'redis',
'store' => 'redis',
'expire' => 7200,
'prefix' => 'sess:',
];
几个注意点:
- Session 的过期时间要和前端登录态一致,否则会出现「页面还显示已登录,接口却返回未登录」;
- 多台机器共享 Session 时必须用同一个 Redis 库与前缀;
- Redis 重启若未开启持久化,所有用户会被登出,做运维操作前先评估影响。
Redis 挂了怎么办
缓存层不应成为单点。读取缓存失败时捕获异常并回退到数据库查询,同时打日志告警;写缓存失败则忽略,不要因为缓存写不进去导致业务失败。
try {
$data = Cache::get($key);
} catch (Throwable $e) {
logger()->warning('缓存读取失败', ['key' => $key, 'error' => $e->getMessage()]);
$data = null;
}
小结
把 Redis 引入项目,收益来自「少查数据库」,风险来自「键管理」与「故障兜底」。键名规范、过期抖动、失败降级这三件事做到位,缓存才算可控。想看数据库层面的配套优化,可以接着读模型关联与预载入。