Yii2用户数据缓存实现合理性及缓存存储方案咨询
Yii2 用户数据缓存实现代码
常量定义
const CacheUserByUid = 'CacheUserByUid_'; const CacheUserByUsername = 'CacheUserByUsername_'; const CacheUserById = 'CacheUserByUsername_'; // 注意:此处存在常量值重复的bug
根据UID获取用户数据
/** * Get user by uid , return user data for user profile * * @param $uid * @return mixed */ public function getUserByUid($uid) { $result = Yii::$app->cache->getOrSet(self::CacheUserByUid . $uid, function () use ($uid) { $result = self::find() ->select([ 'id', 'username', 'email', 'city', 'country', 'name', 'avatar', 'about', 'uid', ]) ->where(['uid' => trim($uid)]) ->one(); if (!empty($result)) { $result->id = (string)$result->id; } return $result; }); return $result; }
根据主键ID获取用户数据
/** * @param $userId * @return mixed */ public function getUserById($userId) { $user = Yii::$app->cache->getOrSet(self::CacheUserById . $userId, function () use ($userId) { return self::findOne($userId); }); return $user; }
根据用户名获取用户数据
/** * Get user by username. Return only for user front info * * @param $username * @return array|\yii\db\ActiveRecord|null */ public function getUserByUsername($username) { $result = Yii::$app->cache->getOrSet(self::CacheUserByUsername . $username, function () use ($username) { $result = self::find()->select([ 'user.id', 'user.city', 'user.country', 'user.name', 'user.avatar', 'user.about', 'user.username' ]) ->where(['username' => $username]) ->one(); if (!empty($result)) { $result->id = (string)$result->id; } // 注意:此处闭包漏写return $result,会导致缓存值为null }); return $result; }
用户更新时清除缓存
/** * @param $data * @param $userId * @return bool * @throws \yii\db\Exception */ public function updateUser($data, $userId) { $user = $this->getUserById($userId); if (!empty($user)) { foreach ($data as $key => $name) { if ($this->hasAttribute($key)) { $user->$key = $name; } } $user->updatedAt = time(); if ($user->save()) { //清除缓存 FileCache::clearCacheByKey(self::CacheUserByUid . $user->uid); FileCache::clearCacheByKey(self::CacheUserByUsername . $user->username); FileCache::clearCacheByKey(self::CacheUserById . $user->id); return true; } } return false; }
清除缓存方法
/** * @param $key */ public static function clearCacheByKey($key) { if (Yii::$app->cache->exists($key)) { Yii::$app->cache->delete($key); } }
技术疑问解答
1. 采用不同key缓存单用户不同查询请求的实现方式是否合理?
这种实现方式是合理的,但需要先修正代码里的两个关键bug:
- 常量
CacheUserById的值与CacheUserByUsername重复,会导致两种查询共用同一缓存key,造成数据混乱; getUserByUsername方法的闭包未返回查询结果,会导致缓存值始终为null。
回到合理性本身:不同查询场景(按UID、ID、用户名)的访问路径和返回字段不同,用独立缓存key可以精准控制缓存的命中与失效,避免不必要的缓存更新。比如用户更新邮箱时,按用户名查询的缓存无需更新(该查询不返回邮箱字段),但按UID查询的缓存必须更新。当前代码中getUserById返回完整模型,另外两个方法返回部分字段,这种差异下用不同key完全必要。
优化方向:
- 可以以返回完整模型的
getUserById缓存为数据源,其他查询方法从该缓存中裁剪字段,减少重复数据库查询; - 给不同缓存设置差异化过期时间,比如高频访问的用户名查询缓存设较短过期时间,低频的UID查询设较长时间,同时保证数据一致性。
2. 使用FileCache存储用户数据是否合适?是否存在更优的缓存存储方案?
FileCache仅适合小型项目或开发环境,生产环境尤其是高并发场景下并不合适,原因如下:
- 读写性能低:文件系统IO速度远低于内存级缓存,并发读写易出现锁等待;
- 缓存管理繁琐:文件缓存的过期清理、空间占用控制不如专业缓存服务便捷;
- 不支持分布式:多服务器部署时,各服务器缓存无法共享,数据一致性难以保障。
更优缓存方案按优先级推荐:
- Redis:内存级键值存储,支持持久化、高并发、分布式,Yii2有官方
yii\redis\Cache扩展,配置简单,能大幅提升缓存性能,是生产环境首选; - Memcached:纯内存级缓存,适合无需持久化的场景,Yii2支持
yii\caching\MemCache; - APCu:PHP内置用户级缓存,适合单服务器部署,无需额外安装服务,性能接近Redis,但不支持分布式。
若暂时无法切换到专业缓存服务,需调整FileCache配置:设置合理缓存目录、开启权限控制、配置自动清理过期缓存规则,避免缓存文件堆积影响性能。
内容的提问来源于stack exchange,提问作者user19145436
相关产品推荐
相关产品推荐

