本文目录导读:

在 PHP 项目中实现数据缓存机制,主要有以下几种常见方案,从简单到复杂、从单机到分布式,你可以根据项目规模和需求选择:
文件缓存(最简单,适合小型项目)
将数据序列化后存储到文件中。
class FileCache {
private $cacheDir;
public function __construct($cacheDir = '/tmp/cache') {
$this->cacheDir = $cacheDir;
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
}
public function set($key, $data, $ttl = 3600) {
$cacheData = [
'expire' => time() + $ttl,
'data' => $data
];
$filename = $this->getFilename($key);
return file_put_contents($filename, serialize($cacheData)) !== false;
}
public function get($key) {
$filename = $this->getFilename($key);
if (!file_exists($filename)) {
return null;
}
$cacheData = unserialize(file_get_contents($filename));
if (time() > $cacheData['expire']) {
unlink($filename);
return null;
}
return $cacheData['data'];
}
private function getFilename($key) {
return $this->cacheDir . '/' . md5($key) . '.cache';
}
}
// 使用示例
$cache = new FileCache();
$cache->set('user_123', ['name' => '张三', 'age' => 30], 300); // 缓存5分钟
$user = $cache->get('user_123');
APCu 缓存(适合单机、快速)
APCu 是 PHP 的内置缓存扩展,直接在内存中操作。
// 安装:pecl install apcu
class ApcuCache {
public function set($key, $data, $ttl = 3600) {
return apcu_store($key, $data, $ttl);
}
public function get($key) {
return apcu_fetch($key) ?: null;
}
public function delete($key) {
return apcu_delete($key);
}
public function clear() {
return apcu_clear_cache();
}
}
// 使用示例
$cache = new ApcuCache();
$cache->set('product_456', ['price' => 99.9, 'stock' => 100], 600);
$product = $cache->get('product_456');
Redis 缓存(主流方案,支持分布式)
Redis 是生产环境最常用的缓存数据库。
安装 predis/predis 或 phpredis 扩展
// composer require predis/predis
use Predis\Client;
class RedisCache {
private $client;
public function __construct($config = []) {
$this->client = new Client($config ?: [
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
}
public function set($key, $data, $ttl = 3600) {
$data = json_encode($data);
return $this->client->setex($key, $ttl, $data);
}
public function get($key) {
$data = $this->client->get($key);
return $data ? json_decode($data, true) : null;
}
public function delete($key) {
return $this->client->del([$key]) > 0;
}
public function exists($key) {
return $this->client->exists($key) === 1;
}
}
// 使用示例
$cache = new RedisCache();
$cache->set('session_abc123', ['user_id' => 1, 'login_time' => time()], 1800);
$session = $cache->get('session_abc123');
常用 Redis 命令及缓存场景
// 1. 基本缓存
$redis->setex('key', 3600, 'value');
// 2. 列表缓存(排行榜)
$redis->lpush('recent_posts', json_encode($post));
$redis->ltrim('recent_posts', 0, 99); // 只保留前100条
// 3. 哈希缓存(对象)
$redis->hmset('user:123', ['name' => '张三', 'email' => 'test@example.com']);
$user = $redis->hgetall('user:123');
// 4. 分布式锁
$lockKey = 'lock:order:123';
$locked = $redis->setnx($lockKey, time() + 10); // 10秒超时
if ($locked) {
// 执行业务逻辑
$redis->del($lockKey); // 释放锁
}
// 5. 计数
$redis->incr('page_views:2024-01-01');
$views = $redis->get('page_views:2024-01-01');
缓存策略模式(通用封装)
将缓存操作抽象为策略模式,便于切换缓存驱动:
interface CacheInterface {
public function set($key, $data, $ttl);
public function get($key);
public function delete($key);
public function clear();
}
class CacheManager {
private $driver;
public function __construct(CacheInterface $driver) {
$this->driver = $driver;
}
// 封装缓存穿透保护
public function remember($key, $ttl, callable $callback) {
$data = $this->driver->get($key);
if ($data !== null) {
return $data;
}
// 避免缓存击穿:加锁
$lockKey = 'lock:' . $key;
if ($this->driver->setnx($lockKey, time() + 5)) {
try {
$data = $callback();
$this->driver->set($key, $data, $ttl);
return $data;
} finally {
$this->driver->delete($lockKey);
}
}
// 等待锁释放
usleep(100000); // 100ms
return $this->remember($key, $ttl, $callback);
}
}
// 使用示例
$manager = new CacheManager(new RedisCache());
$users = $manager->remember('all_users', 300, function() {
// 如果缓存不存在,从数据库查询
return User::all()->toArray();
});
常见问题及解决方案
缓存穿透
问题:大量请求查询不存在的 key,导致数据库压力。
解决:缓存空值(Null Object)或使用布隆过滤器。
function getWithNullObject($key, $ttl, callable $callback) {
$data = $cache->get($key);
if ($data === 'NULL_OBJECT') {
return null;
}
if ($data !== null) {
return $data;
}
$data = $callback();
if ($data === null) {
$cache->set($key, 'NULL_OBJECT', 60); // 短时间缓存空值
} else {
$cache->set($key, $data, $ttl);
}
return $data;
}
缓存雪崩
问题:大量缓存同时过期,导致数据库压力暴增。
解决:设置随机过期时间、多级缓存。
// 添加随机时间避免同时过期
function setWithRandomExpire($key, $data, $baseTtl = 3600) {
$random = mt_rand(0, 300); // 0-5分钟随机偏移
return $cache->set($key, $data, $baseTtl + $random);
}
缓存击穿
问题:热点 key 过期时,大量并发请求访问数据库。
解决:互斥锁(如上文的 remember 方法)或永不过期 + 异步更新。
缓存最佳实践
- 合理设置 TTL:根据数据更新频率
- 缓存粒度控制:不要整体缓存大对象
- 使用缓存前缀:便于批量清除
- 监控缓存命中率:命中率低于 80% 需要优化
- 缓存预热:系统启动时加载热点数据
// 缓存前缀
const CACHE_PREFIX = 'myapp:';
function buildCacheKey($key) {
return CACHE_PREFIX . $key;
}
// 批量删除匹配的缓存(需要 SCAN 命令)
function clearByPattern($pattern) {
$iterator = null;
while ($keys = $redis->scan($iterator, $pattern)) {
if (!empty($keys)) {
$redis->del($keys);
}
}
}
| 方案 | 适用场景 | 性能 | 分布式支持 |
|---|---|---|---|
| 文件缓存 | 小型项目、单机 | 低 | 否 |
| APCu | 单机、需要极快响应 | 极高 | 否 |
| Redis | 中大型项目、集群 | 高 | 是 |
| Memcached | 纯 KV 缓存 | 高 | 是 |
推荐顺序:对于新项目,直接使用 Redis(最通用),小型项目用 APCu 或文件缓存即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播