Yii模型查询缓存三大策略:启用schemaCache可避免重复查information_schema,需配置enableSchemaCache为true、指向有效cache组件并验证key存在;数据缓存用getOrSet实现Cache Aside模式防穿透击穿;Fragment缓存需设合理duration并手动invalidate,否则页面不更新。

Yii模型查询缓存策略直接决定列表页、详情页、API接口的响应快慢——没配缓存时,10条数据可能触发32次SQL;配对了,首屏加载从2.8秒压到0.6秒。这不是调参玄学,而是三类缓存组件的精准落点:数据缓存管结果集、schema缓存管表结构、Fragment缓存管视图片段,每类都得在正确位置开关、设值、清空。
启用并验证schemaCache是否生效
ActiveRecord每次实例化都会查information_schema获取字段类型、主键、索引等元数据,10个模型=10次SELECT,不缓存等于每天白跑几百条无意义查询。
第一步:在db组件配置中明确写'enableSchemaCache' => true,光设schemaCacheDuration不生效。
第二步:确保schemaCache指向一个已启用的缓存组件,例如'schemaCache' => 'cache',否则自动退化为无缓存。
第三步:手动触发一次表结构读取:Yii::$app->db->getSchema()->getTableSchema('user'),然后去Redis里搜schemacache:开头的key——找不到就说明没走缓存,检查cache组件是否启用、网络是否通、前缀是否冲突。
【关键前提】生产环境必须关掉YII_DEBUG,否则schemaCache被强制禁用,无论配置多完美都无效。
用getOrSet实现Cache Aside模式
这是最稳妥的数据缓存写法,天然规避缓存穿透、击穿、雪崩,适合用户中心、商品详情、配置项等读多写少场景。
方法一:直接在Model方法里封装
$data = Yii::$app->cache->getOrSet('user_profile_' . $id, function () use ($id) { return UserProfile::findOne($id); }, 3600);
方法二:控制器中统一收口(推荐)
在Controller里写一个findCachedUser($id)方法,内部用getOrSet,业务逻辑只调这个方法,后续换缓存策略或加熔断都不用改Model。
注意:不要在AR的afterFind()里塞缓存写入逻辑——这会导致每次findOne()都强制写缓存,哪怕数据根本没变,白白消耗Redis带宽。
Fragment缓存控制局部视图更新
当页面里只有“评论区”或“推荐商品列表”需要缓存,其他区域实时渲染时,用FragmentCache比整页缓存更灵活。
在视图文件里包住目标区块:
<?php if (($cache = Yii::$app->view->renderCache('comment_list', ['post_id' => $post->id])) !== false) { echo $cache; return; } ?>
<!-- 原始HTML内容 -->
<?php Yii::$app->view->endCache(); ?>
【致命陷阱】duration设为0表示永不过期,缓存不会自动失效,必须配合Yii::$app->cache->delete('comment_list_' . $post->id)手动清理,否则用户发完新评论页面还是旧数据。
清除模型相关缓存的实操路径
表结构改了、数据批量更新了、缓存键污染了,就得精准清除,不能一flush了事。
① 清schema缓存:执行Yii::$app->db->getSchema()->refresh();,它会清空当前连接下的所有schema缓存key。
② 清单个数据缓存:用Yii::$app->cache->delete('user_profile_123');,别用模糊匹配,避免误删。
③ 清Fragment缓存:调Yii::$app->view->clearCache('comment_list', ['post_id' => 456]);,参数必须和renderCache时完全一致。
④ 批量清某类缓存:如果用了keyPrefix(如'keyPrefix' => 'prod_user_'),直接用Redis命令redis-cli --scan --pattern "prod_user_*" | xargs redis-cli del,比PHP端遍历快10倍。


















