本文目录导读:

在PHP项目中添加缓存层是提升性能的关键手段,以下是系统的实施指南:
缓存架构设计
缓存层级结构
PHP应用
├── 第一层:OPcache(字节码缓存)
├── 第二层:应用级缓存(Redis/Memcached)
├── 第三层:HTTP缓存(CDN/浏览器)
└── 第四层:数据库查询缓存
核心缓存实现方案
OPcache 配置(必做)
// php.ini 推荐配置 opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.revalidate_freq=2 opcache.fast_shutdown=1
Redis 缓存层(主流方案)
基础封装类
<?php
class CacheManager {
private $redis;
private $prefix;
private $ttl;
public function __construct($config) {
$this->redis = new Redis();
$this->redis->connect($config['host'], $config['port']);
$this->prefix = $config['prefix'] ?? 'app:';
$this->ttl = $config['ttl'] ?? 3600;
if (!empty($config['password'])) {
$this->redis->auth($config['password']);
}
}
// 防雪崩:设置随机过期时间
private function getRandomTtl($baseTtl) {
return $baseTtl + rand(0, 300);
}
// 防击穿:互斥锁
public function remember($key, $callback, $ttl = null) {
$ttl = $ttl ?: $this->ttl;
$cacheKey = $this->prefix . $key;
// 读取缓存
$data = $this->redis->get($cacheKey);
if ($data !== false) {
return unserialize($data);
}
// 获取锁,防止缓存击穿
$lockKey = $cacheKey . ':lock';
if ($this->redis->set($lockKey, 1, ['NX', 'EX' => 10])) {
try {
// 执行原始操作
$data = $callback();
// 写入缓存(带随机过期时间)
$this->redis->setex(
$cacheKey,
$this->getRandomTtl($ttl),
serialize($data)
);
return $data;
} finally {
$this->redis->del($lockKey);
}
} else {
// 等待锁释放后重试
usleep(100000); // 100ms
return $this->remember($key, $callback, $ttl);
}
}
// 批量清除
public function flushPattern($pattern) {
$keys = $this->redis->keys($this->prefix . $pattern . '*');
if (!empty($keys)) {
$this->redis->del($keys);
}
}
}
多级缓存策略
<?php
class MultiLevelCache {
private $memoryCache = []; // 内存缓存
private $redis;
public function get($key, $callback, $ttl = 3600) {
// L1: 内存缓存(最快)
if (isset($this->memoryCache[$key])) {
return $this->memoryCache[$key];
}
// L2: Redis缓存
$redisKey = 'cache:' . $key;
$data = $this->redis->get($redisKey);
if ($data !== false) {
$this->memoryCache[$key] = unserialize($data);
return $this->memoryCache[$key];
}
// L3: 数据库或API调用
$data = $callback();
$this->redis->setex($redisKey, $ttl, serialize($data));
$this->memoryCache[$key] = $data;
return $data;
}
}
页面级缓存
<?php
class PageCache {
private $cacheDir;
public function start($cacheKey, $ttl = 300) {
$cacheFile = $this->cacheDir . '/' . md5($cacheKey) . '.html';
// 检查缓存是否有效
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $ttl)) {
readfile($cacheFile);
exit;
}
// 开始输出缓冲
ob_start();
// 注册关闭函数,保存缓存
register_shutdown_function(function() use ($cacheFile) {
$content = ob_get_flush();
file_put_contents($cacheFile, $content);
});
}
}
数据库查询缓存
<?php
class QueryCache {
private $redis;
private $queryCache = [];
public function query($sql, $params = []) {
// 生成查询缓存键
$cacheKey = 'query:' . md5($sql . serialize($params));
// 尝试从缓存获取
$result = $this->redis->get($cacheKey);
if ($result !== false) {
return unserialize($result);
}
// 执行查询(此处假设使用PDO)
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 缓存结果(5分钟)
$this->redis->setex($cacheKey, 300, serialize($result));
return $result;
}
// 数据变更时清除相关缓存
public function invalidate($table) {
$this->redis->del($this->redis->keys('query:*' . $table . '*'));
}
}
缓存常见问题解决方案
缓存穿透
// 使用布隆过滤器
class BloomFilter {
private $redis;
private $key = 'bloom:filter';
public function add($item) {
$hash1 = crc32($item);
$hash2 = md5($item);
// 设置多个位
for ($i = 0; $i < 3; $i++) {
$this->redis->setBit($this->key, $hash1 % 100000, 1);
$this->redis->setBit($this->key, ($hash2 + $i) % 100000, 1);
}
}
public function exists($item) {
// 检查所有位是否都设置
}
}
缓存与数据库一致性
// 使用消息队列异步更新缓存
class CacheUpdateListener {
public function onDataChanged($event) {
// 发送到消息队列
$queue->push('cache:update', [
'table' => $event->table,
'id' => $event->id
]);
}
public function processUpdate() {
while ($message = $queue->pop('cache:update')) {
// 重新构建缓存
$this->rebuildCache($message['table'], $message['id']);
}
}
}
监控与调优
命中率监控
class CacheMonitor {
private $redis;
public function logHit($key) {
$this->redis->incr('stats:hits');
$this->redis->incr('stats:hits:' . $key);
}
public function logMiss($key) {
$this->redis->incr('stats:misses');
$this->redis->incr('stats:misses:' . $key);
}
public function getStats() {
$hits = $this->redis->get('stats:hits') ?: 0;
$misses = $this->redis->get('stats:misses') ?: 0;
return [
'hits' => $hits,
'misses' => $misses,
'rate' => $hits / ($hits + $misses) * 100
];
}
}
本地缓存加速
// 使用APCu作为本地缓存
class LocalCache {
public function get($key) {
return apcu_fetch($key) ?: null;
}
public function set($key, $value, $ttl = 300) {
apcu_store($key, $value, $ttl);
}
}
最佳实践建议
-
缓存策略选择
- 热点数据:Redis缓存,TTL 5-10分钟
- 静态资源:CDN + 浏览器缓存,TTL 1-7天
- 用户会话:Redis,TTL 30分钟
- 数据库结果:Redis,TTL 1-5分钟
-
代码示例(完整实现)
// 初始化缓存配置 $cacheConfig = [ 'host' => '127.0.0.1', 'port' => 6379, 'prefix' => 'mvc:', 'ttl' => 3600 ];
$cache = new CacheManager($cacheConfig);
// 使用缓存获取用户信息 $userId = 123; $user = $cache->remember("user:{$userId}", function() use ($db, $userId) { return $db->query("SELECT * FROM users WHERE id = ?", [$userId]); }, 600);
3. **缓存工具类推荐**
- Symfony Cache组件
- Laravel Cache门面
- PHP-Cache库
通过以上多层缓存策略,可以显著提升PHP应用的响应速度和并发处理能力,关键是根据业务场景选择合适的缓存策略,同时注意处理缓存一致性、雪崩、穿透等问题。