缓存注解不生效的最常见原因是 cache 组件未安装或 Redis 驱动未正确配置,需同时安装 hyperf/cache 和 hyperf/redis,并确保 cache.php 中 driver 指向 RedisDriver、pool 名与 redis.php 一致,且 annotations.php 的 scan 路径包含注解类目录。

缓存注解不生效,根本没走 Redis
最常见原因是 cache 组件没装或驱动没配对。Hyperf 的 #[Cacheable] 不是开箱即用的——它依赖 hyperf/cache 和底层驱动(比如 hyperf/redis)同时就位。
- 执行
composer require hyperf/cache hyperf/redis,缺一不可;只装redis,注解会静默跳过 - 检查
config/autoload/cache.php中'driver'是否指向Hyperf\Cache\Driver\RedisDriver::class,且'pool'值与redis.php里定义的连接池名一致(默认是'default') - 确认
config/autoload/annotations.php的'scan' => ['paths' => [...]]包含了你打注解的类所在目录(如app/Service),否则注解压根不会被扫描到
#[Cacheable] 参数传错导致键名混乱或命中失败
键名生成逻辑依赖参数值和类型,传参稍有偏差就会生成完全不同的 key,缓存形同虚设。
-
#[Cacheable(prefix: 'user:')]中的prefix末尾自带冒号,别写成prefix: 'user:'和prefix: 'user:'重复加冒号 - 方法参数必须可序列化:支持
int、string、array、实现了Serializable或JsonSerializable的对象;传DateTime实例会失败,应先转成string或timestamp - 如果方法返回
null,默认不写入缓存;需要缓存空结果,得显式加empty_ttl参数:#[Cacheable(prefix: 'user:', empty_ttl: 60)]
协程环境下缓存穿透/击穿没防住
注解本身不处理并发请求下的缓存雪崩问题,高并发时多个协程可能同时穿透到 DB。
-
#[Cacheable]默认不做锁保护,纯读场景下并发穿透不可避免 - 若需防击穿,得配合
Redis::setnx()+expire手动加锁,或改用model-cache组件的findFromCache()(它内置了简单锁机制) - 不要在
#[Cacheable]方法里直接调用sleep()或阻塞 DB 查询——协程会被挂起,整个连接池可能卡住;要用co::sleep()或异步查询
缓存更新滞后,业务改了但用户还在看旧数据
注解只管读,不管写。修改数据后不主动失效对应 key,缓存就永远不更新。
-
#[Cacheable]没有自动失效机制,删缓存必须手动做:$this->cache->delete('user:123') - 如果用的是模型缓存(
useCacheableTrait),模型的save()、delete()会自动清理缓存;但自定义 SQL 更新(如DB::table()->where()->update())不会触发,得自己补delete - 异步任务中更新缓存(如队列 Job),务必从容器取
Redis实例,别 new;且注意连接池配置——async_queue.php里要显式指定'redis' => ['pool' => 'default'],否则队列进程用独立连接池,和缓存层物理隔离


















