Cache::get()返回null主因是缓存驱动未切换至Redis而静默回退file驱动;须确认config/cache.php中default为小写'redis'、stores.redis配置完整(含type、select、password)、prefix一致且非空。

Cache::get() 返回 null,大概率不是代码写错了,而是缓存压根没走 Redis —— 你还在用默认的 file 驱动,只是自己没察觉。
为什么 Cache::get() 总是 null?先确认驱动是否真切换了
ThinkPHP 的 Cache 门面不自动加载 Redis 驱动,它只按配置路由到对应 store。即使你写了 Cache::store('redis')->get('key'),只要 config/cache.php 里没正确定义 stores.redis 块,或 default 没设成 'redis',就会静默 fallback 到 file(甚至空逻辑)。
-
default必须是字符串'redis'(大小写敏感,不能是'Redis'或'REDIS') -
stores.redis.type必须为'redis'(TP6 是小写,TP5.1 可接受'Redis',但混用易出错) -
stores.redis.select必须是整数,写成"0"(字符串)会静默失败 -
password字段不能省略,空密码也要显式写'',否则连接可能被拒绝 - 快速验证:运行
php -r "var_dump(cache('test', 'ok', 10));"和php -r "var_dump(cache('test'));",两次都返回'ok'才算通
Cache::store('redis') 和直接 new \Redis() 的 key 对不上
这是最常被骂“缓存失效”的真实原因:前者写的是带前缀的 key(如 think:cache:user_123),后者写的是裸 key(user_123)。两者在 Redis 里根本不在一个 namespace 下。
-
Cache::store('redis')->get('user_123')实际查的是think:cache:user_123(前缀由stores.redis.prefix控制,默认'think:cache:') - 想让命令行调试对得上,要么改前缀:
'prefix' => 'dev_',然后用redis-cli keys "dev_*" - 要么别混用——需要原子操作(
incr、hGetAll)时,用think-redis提供的Redis::instance()获取底层实例,而不是自己new \Redis() - 绝对不要在队列任务里用原生
\Redis写,Web 层用Cache::get()读——注定读不到
expire=0 和 expire=null 的行为完全不同
很多人传 0 想“永不过期”,结果缓存秒删。TP 底层把 0 当作「立即清除」语义,等价于 cache('key', null);而 null 才是真正不设 TTL。
-
Cache::set('k', 'v', 0)→ 立即写入后立刻删,等效于清除操作 -
Cache::set('k', 'v', null)→ Redis 中无 TTL,靠内存淘汰或手动清理 -
Cache::set('k', 'v')→ 使用配置中stores.redis.expire的值(默认 3600 秒) - 生产环境慎用
null:长期堆积会吃光 Redis 内存;建议用大值如31536000(1 年)更可控 - 前端传参带
expire=0,后端别直接透传,要判断并转成null或丢弃
多项目共用 Redis 时 prefix 不是可选项,是必填项
默认前缀 think:cache: 看似安全,但一旦和 Node.js、Python 服务或另一个 TP 项目共用 Redis 实例,key 就全乱套。用 redis-cli keys "*" 根本看不到自己写的 key。
立即学习“PHP免费学习笔记(深入)”;
- 必须设非空
prefix,比如'shop_v2:'或'admin_api:' - 这个
prefix只影响Cache::get()/cache()等门面调用,不影响你手动用Redis::instance()->set()写的 key - 前缀为空时,若 key 含空格、斜杠、中文等,TP 不校验,Redis 虽支持,但后续排查成本极高
- 不同环境用不同前缀:开发用
'dev_',测试用'test_',上线用'prod_'



















