@cacheable的ttl未生效是因优先级覆盖:注解值>cache.php配置>redisdriver默认值;若配置文件设ttl=>0或null,则缓存永不过期,且set覆写会清除原ttl。

Cacheable注解的ttl没生效,其实是被覆盖了
Hyperf里@Cacheable的ttl参数不是“写了就用”,它只在没被上层配置覆盖时才起效。实际生效值按优先级逐层 fallback:注解中显式写的ttl > config/autoload/cache.php里default驱动的'ttl' => 3600 > RedisDriver类内置默认值(3600秒)。如果你在注解里没写ttl,但配置文件里设了ttl => 0或null,那缓存就真成永不过期了。
常见错误现象:
- 改了注解里的
ttl: 1800,但Redis里查TTL还是-1(永不过期) - 缓存键一直存在,
redis-cli执行TTL key_name返回-1
实操建议:
- 先确认
config/autoload/cache.php中default驱动是否误配了'ttl' => 0或'ttl' => null - 检查是否启用了
model-cache组件——它的empty_model_ttl和@Cacheable互不干扰,别混淆 - 用
redis-cli连上后执行PTTL your_cache_key,看返回值是否为负数(-1=永不过期,-2=key不存在)
Redis本身没启用过期策略?不是,是持久化配置掩盖了问题
Redis的key过期机制(惰性+定期删除)默认始终开启,不需要额外开关。所谓“无法自动过期”,99%不是Redis没干活,而是你根本没给key设过期时间——或者设了又被后续操作清掉了。
关键陷阱:
-
SET或GETSET覆写已有key时,原EXPIRE会丢失 - 用
HSET/INCR等非覆写命令修改value,EXPIRE保留;但Hyperf的RedisDriver底层用SET写缓存,所以每次刷新都重置TTL - Redis重启后,若没开AOF或RDB,所有过期时间信息丢失,key变成永久有效(但内容还在)
实操建议:
- 检查
config/autoload/redis.php中'options'是否配置了'appendonly' => 'yes'和'appendfsync' => 'everysec' - 避免在业务代码里手动调
Redis::set($key, $val),改用Redis::setex($key, $ttl, $val)或Redis::expire($key, $ttl)显式控制 - 用
redis-cli --scan --pattern 'your_prefix:*' | xargs -I{} redis-cli PTTL {}批量抽查过期状态
缓存键被重复使用,导致“看似没过期”
Hyperf的@Cacheable如果value里没带占位符,所有调用都会打到同一个key上。比如value: '_123'写死,那无论传什么$id,缓存键永远是user:_123,看起来像“一直有效”,其实是被不断覆盖。
更隐蔽的问题是参数类型不一致:public function info(int $id),但调用时传了字符串'123',Hyperf反射生成的缓存键是user:_123,而实际执行时因类型校验失败,AOP切面可能跳过缓存逻辑,直接查DB并重新写入——结果就是键存在、TTL正常,但内容永远是旧的。
实操建议:
- 强制
value含占位符:value: '_{id}',而非value: '_123' - 数组参数必须用
_{params.id},不能用_{id} - 开启
Hyperf\Di\Aop\ProxyManager的debug日志,grepCacheableAspect确认切面是否触发 - 用
var_dump($arguments)在hashArguments方法里看实际参与键生成的参数值
高频失效时,Redis的过期策略被误读为“不工作”
Redis的过期是异步的:惰性删除(访问时检查)+ 定期抽样删除(每秒10次,每次最多25个key)。这意味着大量key在同一秒过期时,不会立刻全删,而是分批清理。如果你在过期临界点用KEYS扫出来还看到key,不代表没过期,只是还没被抽样到。
真正影响业务的是“集中失效”——比如所有用户缓存都设ttl: 3600,整点一到请求涌进,大量key同时过期,Redis来不及删,DB瞬间被打穿。
实操建议:
- 错开TTL:
3600 + random_int(0, 600),或基于ID哈希偏移:3600 + abs(crc32($uid) % 600) - 禁用
KEYS命令查过期key,改用SCAN配合PTTL脚本批量验证 - 监控Redis的
expired_keys指标(INFO stats),比肉眼查key更准
最常被忽略的一点:缓存生命周期必须和业务节奏对齐。比如订单状态变更频繁,缓存TTL设再短,若DB更新后没及时失效,照样脏;反过来,城市列表这种静态数据,设24小时+随机偏移,比每5分钟刷一次更稳。











