ThinkPHP 不干预 Redis 过期清理,实际由 Redis 惰性删除+定期删除机制决定;需合理配置 hz(建议50~100)、避免 Cache::set 误用无 TTL、禁用 KEYS 扫描,依赖 Redis 自身机制保障稳定性。

ThinkPHP 本身不干预 Redis 的过期 key 清理逻辑,它只是通过 phpredis 或 Predis 扩展调用 Redis 命令。真正决定“过期 key 是否被及时清理”的,是 Redis 自身的惰性删除 + 定期删除机制,以及你的配置和使用方式。盲目在 ThinkPHP 层加定时扫描 KEYS * 或轮询 TTL,反而容易拖垮性能。
hz 配置太低导致定期删除形同虚设
Redis 默认 hz 10,即每秒只做 10 次定时任务,每次最多检查 20 个带过期时间的 key。
如果你的业务每秒新增数百个短期缓存(比如验证码、临时 token),而 hz 还是 10,那大量过期 key 会堆积在内存里,used_memory_human 持续上涨,但 mem_fragmentation_ratio 看似正常——其实是“假健康”。
- 生产环境建议将
hz调至50~100(需压测验证 CPU 影响) - 修改方式:在
redis.conf中设置hz 100,然后redis-cli CONFIG REWRITE或重启 - 注意:
hz超过 100 后收益急剧下降,且可能挤占主线程响应时间
ThinkPHP 写缓存时没设 TTL 或用了错误方法
ThinkPHP 的 Cache::set() 默认不设过期时间,等价于永不过期;即使你传了 $expire,底层是否真发了 SETEX 或 EXPIRE,取决于驱动实现和参数类型。
常见陷阱:
- 用
Cache::set('key', $value, 0):部分版本会忽略 0,变成永不过期 - 传浮点数
$expire(如3600.5):phpredis 不支持毫秒级 TTL,会被截断为整数,但 Predis 可能报错或静默失败 - 使用
remember()但未指定ttl参数:Laravel 风格语法在 ThinkPHP 中需确认版本支持
正确做法(以 ThinkPHP 6.x + phpredis 为例):
- 显式用
$redis->setex($key, $ttl, $value),绕过框架封装,确保原子性 - 若必须走
Cache::set(),验证返回值并配合Cache::get()后立刻查$redis->ttl($key)确认是否生效
误信 volatile-ttl 能“主动清濒死 key”
很多开发者看到策略名里有 ttl,就以为 Redis 会优先删“马上过期”的 key。实际上:
-
volatile-ttl只在内存打满(触发freeMemoryIfNeeded())且有写入操作时才启动 - 它每次只随机采样
maxmemory-samples(默认 5)个设置了 TTL 的 key,选其中TTL最小的删一个 - 即使 key 剩 1 秒,只要没写入、没到
maxmemory,它就完全不工作
所以靠 volatile-ttl 优化内存占用是无效的。真正起作用的是定期删除(hz)和惰性删除(访问触发)。
不要在 ThinkPHP 应用层写定时脚本扫 KEYS *
有人在 command 里写 PHP 脚本,每分钟执行:
$keys = $redis->keys('*');
foreach ($keys as $key) {
if ($redis->ttl($key) del($key);
}
这非常危险:
-
KEYS *是阻塞命令,在大数据量下直接卡死 Redis - 单次遍历可能耗时数秒,期间所有请求排队
-
TTL返回-2表示 key 不存在或已逻辑过期,但无法区分二者,容易误删正在写的 key
替代方案只有两个:
- 调高
hz,让 Redis 自己更勤快地抽样清理 - 用
SCAN分批处理(但依然不推荐应用层做,应交由 Redis 机制)
真正需要人工干预的场景极少,通常是发现 redis-cli --stat 显示 expired_keys 指标持续飙升(>1000/s),才说明定期删除严重跟不上节奏——这时该查是不是 hz 太低,或者业务在集中写短 TTL 缓存却没做随机化。
最常被忽略的一点:过期 key 清理不是“越快越好”,而是“够用且稳定”。强行追求实时清理,往往换来更高的 CPU 波动和更差的 RT 稳定性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











