You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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速度远低于内存级缓存,并发读写易出现锁等待;
  • 缓存管理繁琐:文件缓存的过期清理、空间占用控制不如专业缓存服务便捷;
  • 不支持分布式:多服务器部署时,各服务器缓存无法共享,数据一致性难以保障。

更优缓存方案按优先级推荐:

  1. Redis:内存级键值存储,支持持久化、高并发、分布式,Yii2有官方yii\redis\Cache扩展,配置简单,能大幅提升缓存性能,是生产环境首选;
  2. Memcached:纯内存级缓存,适合无需持久化的场景,Yii2支持yii\caching\MemCache;
  3. APCu:PHP内置用户级缓存,适合单服务器部署,无需额外安装服务,性能接近Redis,但不支持分布式。

若暂时无法切换到专业缓存服务,需调整FileCache配置:设置合理缓存目录、开启权限控制、配置自动清理过期缓存规则,避免缓存文件堆积影响性能。

内容的提问来源于stack exchange,提问作者user19145436

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 14:10:37