Hyperf 2.2 中 @Cacheable 默认不生效,需启用 SCAN_CACHEABLE 并配置 annotations.php 的 'cacheable' => true,否则 AnnotationCollector 每次重新扫描文件导致性能骤降。

Hyperf 2.2 中 @Cacheable 注解默认不生效,必须手动开启注解扫描缓存(SCAN_CACHEABLE),否则每次请求都重新解析 PHP 文件,性能断崖式下跌。
为什么 @Cacheable 加了却没走缓存?
Hyperf 2.2+ 默认关闭注解运行时缓存,即使你写了 #[Cacheable(prefix: 'user', ttl: 3600)],框架也不会自动缓存方法返回值——它连注解都没“记住”。根本原因是 AnnotationCollector 每次都重新扫描文件,没复用已解析结果。
- 检查
config/autoload/annotations.php是否含'cacheable' => true(不是enable) - 确认环境变量
SCAN_CACHEABLE=1已设置(Docker 或 .env 都要配) - 首次启动后,检查
runtime/container/annotation/下是否生成了*.php缓存文件;没有就说明没生效 - 若用代码加密部署,
runtime/container/proxy/和.cache文件必须一并加密,且部署路径需与scan.cache中记录的绝对路径一致
@Cacheable 的 value 参数怎么写才安全?
它不是简单的字符串拼接模板,而是基于 StringHelper::format() 的动态键生成逻辑。数组参数直接传入会导致键冲突或超长,比如 ['status' => 1, 'type' => 'vip'] 和 ['type' => 'vip', 'status' => 1] 会生成两个不同 key,破坏缓存一致性。
- 避免裸传数组:不要写
value: "_{filters}",其中$filters是 array - 推荐显式哈希:用
value: "_{md5(serialize($filters))}"(需确保$filters可序列化且无资源句柄) - 更健壮的做法是自定义
StringHelper子类,在format()中对数组调用ksort() + json_encode(..., JSON_UNESCAPED_UNICODE)再 hash - 注意:
value中不能出现:,否则 Redis 键层级会被错误切分(如user:_{"id":1}:会被当三级目录)
缓存穿透防护在 @Cacheable 里怎么启用?
它本身不处理空值缓存,只有配合 hyperf/model-cache 的 empty_model_ttl 才生效;纯服务层注解需手动兜底。
- 如果你用的是模型缓存(
User::findFromCache(999)),确保config/autoload/databases.php中对应 DB 配置启用了'empty_model_ttl' => 3600 - 如果是普通服务方法,
@Cacheable不会自动缓存null或[],必须自己判断:#[Cacheable(prefix: 'search', ttl: 300)] public function search(array $params) { $result = $this->doSearch($params); if (empty($result)) { // 手动塞一个空标记,避免反复穿透 $this->cache->set('search:' . md5(serialize($params)), [], 300); return []; } return $result; } - 别依赖
ttl小于 60 秒来“防穿透”,Redis 过期是惰性+定期混合策略,短 TTL 在高并发下依然可能被击穿
真正容易被忽略的是:注解缓存和 Redis 连接池是两套独立生命周期。哪怕 @Cacheable 生效了,如果 config/autoload/redis.php 里 min_connections 设为 0,协程密集调用时仍会卡在连接获取上——缓存键算得再快,也得等 Redis 连上才能读。


















