Hyperf模型缓存必须使用Redis,因model-cache组件硬编码依赖RedisHandler::class,不支持FileHandler等其他驱动;findFromCache仅读缓存不自动回填,需配合Cacheable trait的save()或refreshCache()手动写入;缓存键格式mc:%s:m:%s:%s:%s不可随意修改,否则导致命中失败与清理失效。

Hyperf 模型缓存必须用 Redis,别试其他驱动
Hyperf 的 model-cache 组件硬编码依赖 Redis,RedisHandler::class 是唯一支持的缓存处理器。哪怕你配置了 FileHandler 或 MemoryHandler,启动时会直接报错:Class "Hyperf\ModelCache\Handler\FileHandler" not found。这不是配置问题,是源码层面限制 —— 所有缓存键生成、Lua 脚本执行、空值兜底逻辑都只适配 Redis 的原子操作。
常见错误现象:本地开发想用 file 缓存绕过 Redis 依赖,结果 User::findFromCache(1) 抛出 Connection refused 或 Redis server went away,其实是框架根本没走 fallback 分支,而是直接尝试连接 Redis。
- 必须安装
hyperf/redis和hyperf/model-cache,缺一不可 -
config/autoload/redis.php中的连接池名(如default)要和数据库配置里cache.pool值一致 - 若用哨兵或集群模式,需在
redis.php中显式配置'sentinel' => true或'cluster' => true,否则RedisHandler会默认走单机模式并连接失败
findFromCache 不等于自动缓存,它只读不写
findFromCache 和 findManyFromCache 是“带缓存读取”,不是“读写缓存”。它们的行为是:先查 Redis,命中则返回;未命中则查 DB,但**不会自动把结果回填到缓存里** —— 这点和很多人直觉相反。
真实使用场景下,如果你只调用 User::findFromCache(1),第一次必然穿透 DB,且后续请求仍会穿透,除非你手动触发写缓存。正确做法是配合 Cacheable trait 的 save() 或 refreshCache():
- 模型更新后,调用
$user->save()会自动更新缓存(前提是模型已 useCacheable) - 主动刷新某条记录缓存:
User::query()->where('id', 1)->first()->refreshCache() - 批量刷新:
User::refreshCacheByIds([1,2,3]),注意这个方法只对主键查询有效
漏掉这步,缓存就形同虚设 —— 高频查询反而因每次都穿透 DB + 多余 Redis 查询而更慢。
缓存键格式固定,改 cache_key 会影响所有旧缓存失效
默认缓存键是 mc:%s:m:%s:%s:%s,四个占位符依次为:prefix、表名、主键字段名、主键值。例如 mc:default:m:user:id:1。这个格式被硬写在 RedisHandler::getKey() 里,且所有缓存读写、删除逻辑都依赖它。
如果你在 database.php 里把 cache_key 改成 user:%s,会导致:
- 旧缓存永远无法命中(key 格式变了)
-
refreshCache()写入新 key,但findFromCache()还按老格式读,彻底断连 - 清空缓存时,
RedisHandler::clear()用的是原格式 scan,新 key 不会被清理
所以生产环境严禁随意修改 cache_key。如需迁移,必须双写过渡 + 手动清理旧 key,或者用 redis-cli --scan --pattern "mc:*" 批量删。
高频查询别只靠 findFromCache,得组合预热 + TTL 分层
单纯依赖 findFromCache 应对每秒数百次的用户详情查询,容易出现缓存雪崩或击穿。关键不在“有没有缓存”,而在“缓存怎么分层扛住峰值”。
实操建议:
- 对核心 ID 查询(如
User::findFromCache($id)),设置较长ttl(如86400),但开启empty_model_ttl(如60),避免缓存穿透 - 启动时用命令行预热热点数据:
php bin/hyperf.php model:cache:preload user --ids=1,2,3,100(需自定义命令或监听WorkerStart事件) - 对列表类高频查询(如首页推荐),不用模型缓存,改用注解缓存:
#[Cacheable(prefix: "home_users", ttl: 300)],避免主键缓存机制的局限 - 监控 Redis 的
keyspace_hits / keyspace_misses比率,低于 0.9 就说明穿透严重,得查是不是use_default_value开关没开导致空值不缓存
最易被忽略的点:模型缓存的 TTL 是全局配置,没法 per-query 动态调整。如果某个用户资料更新极频繁,又必须实时,那就别走模型缓存,直接查库 + 手动 setex 控制粒度。


















