PHP项目怎么解决数据缓存过期?

wen PHP项目 10

本文目录导读:

PHP项目怎么解决数据缓存过期?

  1. 主动过期(TTL) - 最常用
  2. 被动过期(懒加载) - 最可靠
  3. 主动刷新(Write-Through)
  4. 缓存预热 + 定时刷新
  5. 二级缓存策略
  6. 锁机制(防止缓存雪崩)
  7. 缓存版本控制
  8. 最佳实践建议

在PHP项目中解决数据缓存过期问题,通常有以下几种主流方案,按推荐程度排序:

主动过期(TTL) - 最常用

在写入缓存时设置过期时间,到期自动失效。

// Redis 示例
$redis->setex('user:123', 3600, serialize($userData)); // 1小时过期
// Memcached 示例
$memcached->set('user:123', $userData, 3600);
// 文件缓存示例(手动实现)
function setCache($key, $data, $ttl = 3600) {
    $cacheFile = '/tmp/cache/' . md5($key);
    $content = [
        'expire' => time() + $ttl,
        'data' => $data
    ];
    file_put_contents($cacheFile, serialize($content));
}

被动过期(懒加载) - 最可靠

读取时检查过期时间,过期则重新生成。

function getCachedData($key, $callback, $ttl = 3600) {
    $cached = Cache::get($key);
    if ($cached === null) {
        // 缓存不存在或已过期,重新生成
        $data = $callback();
        Cache::set($key, $data, $ttl);
        return $data;
    }
    return $cached;
}
// 使用示例
$users = getCachedData('users:list', function() {
    return DB::table('users')->get();
}, 3600);

主动刷新(Write-Through)

更新数据时同时更新缓存。

function updateUser($userId, $data) {
    // 1. 先更新数据库
    DB::table('users')->where('id', $userId)->update($data);
    // 2. 同时更新缓存
    $user = DB::table('users')->find($userId);
    Cache::set("user:$userId", $user, 3600);
    // 3. 可选:清理相关列表缓存
    Cache::forget('users:list');
}

缓存预热 + 定时刷新

// 1. 定时任务(Cron)
// */5 * * * * php /path/to/preheat_cache.php
// preheat_cache.php
function preheatPopularCache() {
    $popularUsers = DB::table('users')
        ->where('login_count', '>', 1000)
        ->get();
    foreach ($popularUsers as $user) {
        Cache::set("user:{$user->id}", $user, 3600);
    }
    // 预热首页数据
    $hotData = DB::table('articles')
        ->where('status', 'published')
        ->orderBy('views', 'desc')
        ->limit(20)
        ->get();
    Cache::set('home:hot_articles', $hotData, 3600);
}

二级缓存策略

class TwoLevelCache {
    private $local = []; // 本地内存缓存(最快)
    public function get($key) {
        // 一级:本地内存
        if (isset($this->local[$key]) && $this->local[$key]['expire'] > time()) {
            return $this->local[$key]['data'];
        }
        // 二级:Redis/共享缓存
        $data = Redis::get($key);
        if ($data) {
            $this->local[$key] = [
                'data' => $data,
                'expire' => time() + 60 // 本地缓存1分钟
            ];
            return $data;
        }
        return null;
    }
}

锁机制(防止缓存雪崩)

function getExpensiveData($key, $ttl = 3600) {
    $data = Cache::get($key);
    if ($data === null) {
        // 获取锁,防止多个请求同时重建缓存
        $lockKey = $key . ':lock';
        $lock = Cache::lock($lockKey, 10); // 10秒超时
        if ($lock->get()) {
            try {
                // 双重检查
                $data = Cache::get($key);
                if ($data === null) {
                    $data = heavyDatabaseQuery();
                    Cache::set($key, $data, $ttl);
                }
            } finally {
                $lock->release();
            }
        } else {
            // 等待锁释放后重试
            usleep(100000); // 等100ms
            return $this->getExpensiveData($key, $ttl);
        }
    }
    return $data;
}

缓存版本控制

class VersionedCache {
    private $versionKey = 'cache:version';
    public function get($key) {
        $version = Cache::get($this->versionKey) ?? 1;
        return Cache::get("{$key}:v{$version}");
    }
    public function set($key, $data, $ttl = 3600) {
        $version = Cache::get($this->versionKey) ?? 1;
        Cache::set("{$key}:v{$version}", $data, $ttl);
    }
    public function invalidateAll() {
        Cache::increment($this->versionKey);
    }
}

最佳实践建议

选择合适的过期策略

// 对于不常变化的数据(如配置)
Cache::set('site_config', $config, 86400); // 24小时
// 对于用户会话数据
Cache::set("session:$userId", $session, 1800); // 30分钟
// 对于统计数据
Cache::set('daily_stats', $stats, 300); // 5分钟更新

监控缓存命中率

class MonitoredCache {
    private $hits = 0;
    private $misses = 0;
    public function get($key) {
        $data = parent::get($key);
        if ($data === null) {
            $this->misses++;
        } else {
            $this->hits++;
        }
        return $data;
    }
    public function getHitRate() {
        $total = $this->hits + $this->misses;
        return $total > 0 ? ($this->hits / $total) * 100 : 0;
    }
}

错误处理

function getWithFallback($key, $callback, $ttl = 3600) {
    try {
        $data = Cache::get($key);
        if ($data !== null) {
            return $data;
        }
    } catch (\Exception $e) {
        // 缓存系统不可用时,直接查询数据库
        Log::warning('Cache unavailable, falling back to DB');
    }
    return $callback();
}
  • 80%的场景:使用简单的 TTL + 懒加载模式
  • 高并发场景:需要加锁机制防止缓存雪崩
  • 数据一致性要求高:使用 Write-Through 模式
  • 大规模系统:考虑二级缓存 + 版本控制

选择方案时要考虑:

  1. 数据更新频率
  2. 数据一致性要求
  3. 系统并发量
  4. 缓存资源成本

抱歉,评论功能暂时关闭!