cache() 默认走 file 驱动而非 Redis,$expire 单位为秒但仅在 Redis 驱动启用且配置正确时生效;非法类型、非原子操作及配置疏漏是常见踩坑点。

cache() 助手函数在 ThinkPHP 6 中默认走的是 file 驱动,不是 Redis —— 这是第一个也是最常被忽略的“单位踩坑”源头。你以为写了 cache('key', $value, 3600) 就设了 1 小时过期,结果数据全写进了本地文件,根本没进 Redis,更谈不上 TTL 单位是否正确。
真正影响过期时间单位的,是 底层驱动行为 + 参数类型 + 配置是否生效,而不是你传了个整数就万事大吉。
✅ 明确 cache() 的过期时间单位规则
-
cache($key, $value, $expire)中的$expire单位是 秒(整数),这是正确的; - 但前提是:当前使用的缓存驱动必须是 Redis,且该驱动真实支持并正确处理
$expire; - 若配置的是 file 或 array 驱动,
$expire会被忽略或静默失效(file 驱动会把过期逻辑转为文件修改时间判断,不可靠;array 驱动压根不持久化)。
⚠️ 注意:
cache('key', $value, 0)在多数 Redis 驱动下等价于「永不过期」,不是“立刻过期”。想立刻失效,请用Cache::delete()。
✅ 确保 Redis 驱动真正启用并接管 cache()
检查 config/cache.php:
立即学习“PHP免费学习笔记(深入)”;
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'Redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 3,
'expire' => 3600, // ← 这是 store 级默认过期,不影响单次 cache() 调用
],
],关键点:
-
'default' => 'redis'必须明确指定; - 不要只改
cache()调用,却忘了配stores.redis; - 开发环境别用
'default' => 'file'测试 Redis 行为——它不会报错,但完全不走 Redis。
验证是否生效的小技巧:
// 强制走 redis store 并写一个带 TTL 的 key
Cache::store('redis')->set('test_ttl', 'ok', 10);
var_dump(Redis::connect('127.0.0.1', 6379)->ttl('test_ttl')); // 应返回 8~10 之间的整数✅ 避开浮点数、字符串、负数等非法 $expire 类型
ThinkPHP 的 Redis 驱动(基于 phpredis)只接受 int 类型 TTL:
-
cache('k', 'v', 3600)→ ✅ 正确 -
cache('k', 'v', 3600.5)→ ❌ 截断为3600,但部分版本可能触发警告或静默失败 -
cache('k', 'v', '3600')→ ❌ 字符串不被识别,可能变成永不过期 -
cache('k', 'v', -1)→ ❌ 多数驱动视作无效,等同于不设 TTL
建议统一做类型断言:
$expire = (int) $ttl;
if ($expire <= 0) {
$expire = 3600; // 设安全兜底值
}
cache('key', $value, $expire);✅ 不要用 cache() 做原子性要求高的操作(如限流、分布式锁)
cache() 是便利封装,但:
- 它不保证
set + expire原子性(旧版驱动可能分两条命令发); -
cache('key', $value, $ttl)在某些场景下可能先 set 再 expire,中间若进程崩溃,key 就变成永不过期; - 限流、抢购类逻辑,应直接调用 Redis 原生命令或 Lua 脚本。
推荐替代方式:
// 真正原子的 setex
Cache::store('redis')->handler()->setex('key', 60, 'value');
// 或用 eval 执行 Lua(强一致性)
Cache::store('redis')->handler()->eval(
"return redis.call('setex', KEYS[1], ARGV[1], ARGV[2])",
1, 'key', 60, 'value'
);✅ 生产环境额外检查项
- Redis 的
hz配置是否调高(建议50~100),否则大量短期 key 过期后迟迟不释放内存; - 是否误用
Cache::set('key', $val, 0)以为是“立即过期”,实际是“永不过期”; - 是否在
remember()中漏传$ttl,导致缓存永不更新; - 是否用
KEYS *扫描 key 判断过期 —— 禁止!改用SCAN+TTL组合,或依赖 Redis 自身清理机制。
不复杂但容易忽略。



















