本文目录导读:

在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 模式
- 大规模系统:考虑二级缓存 + 版本控制
选择方案时要考虑:
- 数据更新频率
- 数据一致性要求
- 系统并发量
- 缓存资源成本