本文目录导读:

我来详细介绍PHP中推荐结果缓存的最佳实践方案。
缓存架构设计
基础缓存类
<?php
namespace App\Cache;
use Redis;
use Predis\Client;
class RecommendationCache
{
private $redis;
private $ttl;
// 缓存键前缀
const KEY_PREFIX = 'rec:';
// 不同场景的缓存时间
const TTL_HOT = 3600; // 1小时
const TTL_NORMAL = 1800; // 30分钟
const TTL_COLD = 7200; // 2小时
public function __construct(Redis $redis, $ttl = self::TTL_NORMAL)
{
$this->redis = $redis;
$this->ttl = $ttl;
}
/**
* 获取推荐结果(带缓存击穿保护)
*/
public function getRecommendations($userId, $type = 'home', $limit = 10)
{
$cacheKey = $this->buildKey($userId, $type, $limit);
$cacheKeyLock = $cacheKey . ':lock';
// 1. 尝试从缓存获取
$cached = $this->redis->get($cacheKey);
if ($cached !== false) {
return json_decode($cached, true);
}
// 2. 缓存未命中,检查锁
if ($this->redis->setnx($cacheKeyLock, 1)) {
// 获取锁成功,设置过期时间防止死锁
$this->redis->expire($cacheKeyLock, 10);
try {
// 3. 执行查询算法
$data = $this->getRecommendationsFromAlgorithm($userId, $type, $limit);
// 4. 写入缓存
$this->redis->setex(
$cacheKey,
$this->ttl,
json_encode($data)
);
// 5. 删除锁
$this->redis->del($cacheKeyLock);
return $data;
} catch (\Exception $e) {
// 发生异常,删除锁
$this->redis->del($cacheKeyLock);
// 返回兜底数据
return $this->getFallbackData($userId, $type, $limit);
}
} else {
// 等待锁释放后重试
usleep(200000); // 200毫秒
return $this->getRecommendations($userId, $type, $limit);
}
}
/**
* 构建缓存键
*/
private function buildKey($userId, $type, $limit)
{
return self::KEY_PREFIX . $type . ':' . $userId . ':' . $limit;
}
/**
* 获取备选数据(降级策略)
*/
private function getFallbackData($userId, $type, $limit)
{
// 返回热门内容或默认推荐
return $this->redis->get(self::KEY_PREFIX . 'hot:' . $type);
}
}
多级缓存实现
<?php
namespace App\Cache;
class MultiLevelCache
{
private $memoryCache; // 内存缓存
private $redisCache; // Redis缓存
private $databaseCache; // 数据库缓存
private $memoryTTL = 60; // 内存缓存60秒
private $redisTTL = 600; // Redis缓存10分钟
/**
* 三级缓存获取数据
*/
public function getData($key)
{
// 1. 内存缓存(最快)
$data = $this->getFromMemory($key);
if ($data !== null) {
return $data;
}
// 2. Redis缓存
$data = $this->getFromRedis($key);
if ($data) {
// 回填内存缓存
$this->setToMemory($key, $data);
return $data;
}
// 3. 数据库查询
$data = $this->getFromDatabase($key);
if ($data) {
$this->setToMemory($key, $data);
$this->setToRedis($key, $data);
}
return $data;
}
/**
* 内存缓存
*/
private function getFromMemory($key)
{
// 使用APCu或本地数组
if (function_exists('apcu_fetch')) {
$data = apcu_fetch('rec_' . $key);
return $data !== false ? $data : null;
}
// 简单的内存数组(单进程)
static $memory = [];
if (isset($memory[$key])) {
if (time() - $memory[$key]['time'] < $this->memoryTTL) {
return $memory[$key]['data'];
}
}
return null;
}
private function setToMemory($key, $data)
{
if (function_exists('apcu_store')) {
apcu_store('rec_' . $key, $data, $this->memoryTTL);
} else {
static $memory = [];
$memory[$key] = ['data' => $data, 'time' => time()];
}
}
/**
* Redis缓存
*/
private function getFromRedis($key)
{
try {
$redis = $this->getRedis();
$data = $redis->get('rec_redis_' . $key);
return $data ? unserialize($data) : null;
} catch (\Exception $e) {
// Redis异常时返回null,触发数据库查询
error_log('Redis error: ' . $e->getMessage());
return null;
}
}
private function setToRedis($key, $data)
{
try {
$redis = $this->getRedis();
$redis->setex(
'rec_redis_' . $key,
$this->redisTTL,
serialize($data)
);
} catch (\Exception $e) {
error_log('Redis error: ' . $e->getMessage());
}
}
/**
* 数据库查询
*/
private function getFromDatabase($key)
{
// 伪代码:数据库查询逻辑
return ['data' => 'database result'];
}
}
缓存更新策略
<?php
namespace App\Cache;
class CacheManager
{
private $redis;
/**
* 主动清除缓存
*/
public function invalidateUserCache($userId)
{
$pattern = "rec:*:{$userId}:*";
$keys = $this->redis->keys($pattern);
if (!empty($keys)) {
$this->redis->del($keys);
}
}
/**
* 异步更新缓存
*/
public function asyncUpdateCache($key, $callable)
{
// 使用消息队列异步更新
$queue = 'cache_update_queue';
$this->redis->lpush($queue, json_encode([
'key' => $key,
'type' => 'update',
'callback' => $callable
]));
}
/**
* 批量更新缓存
*/
public function batchUpdate($items)
{
$pipeline = $this->redis->pipeline();
foreach ($items as $item) {
$key = $item['key'];
$data = $item['data'];
$ttl = isset($item['ttl']) ? $item['ttl'] : 3600;
$pipeline->setex($key, $ttl, json_encode($data));
}
return $pipeline->exec();
}
/**
* 预热缓存
*/
public function warmUp($keys)
{
foreach ($keys as $key) {
if (!$this->redis->exists($key)) {
// 生成数据并写入缓存
$this->generateData($key);
}
}
}
}
缓存穿透解决方案
<?php
namespace App\Cache;
class CacheProtection
{
private $redis;
/**
* 布隆过滤器防穿透
*/
public function getAndFilter($userId, $type)
{
$key = "rec_filter:{$type}:{$userId}";
// 使用布隆过滤器检查是否可能存在
if (!$this->bloomFilterCheck($userId)) {
return [];
}
$cacheKey = "rec:{$type}:{$userId}";
$data = $this->redis->get($cacheKey);
if (!$data) {
// 空值缓存,防止穿透
$this->redis->setex($cacheKey, 300, json_encode([]));
return [];
}
return json_decode($data, true);
}
/**
* 布隆过滤器实现
*/
private function bloomFilterCheck($userId)
{
$bloomKey = 'user_bloom_filter';
$bitmapSize = 5000000; // 5百万位
// 简单的哈希函数
$hash1 = crc32($userId) % $bitmapSize;
$hash2 = md5($userId) % $bitmapSize;
$hash3 = sha1($userId) % $bitmapSize;
return $this->redis->getbit($bloomKey, $hash1) &&
$this->redis->getbit($bloomKey, $hash2) &&
$this->redis->getbit($bloomKey, $hash3);
}
/**
* 随机过期时间防止雪崩
*/
public function getWithRandomTTL($key, $ttlBase = 3600)
{
$data = $this->redis->get($key);
if ($data === false) {
// 在TTL基础上增加随机时间
$randomTTL = $ttlBase + mt_rand(0, 300);
// 重建数据...
$data = $this->rebuildData($key);
$this->redis->setex($key, $randomTTL, $data);
}
return $data;
}
}
性能优化技巧
<?php
namespace App\Cache;
class PerformanceOptimizer
{
private $redis;
/**
* 批量获取推荐
*/
public function batchGetRecommendations($userIds, $type = 'home')
{
$keys = [];
foreach ($userIds as $userId) {
$keys[] = "rec:{$type}:{$userId}";
}
// 使用mget批量获取
$results = $this->redis->mget($keys);
// 处理未命中的请求
$missKeys = [];
$missIndices = [];
foreach ($results as $index => $result) {
if ($result === false) {
$missKeys[] = $keys[$index];
$missIndices[] = $index;
}
}
// 批量生成并缓存
if (!empty($missKeys)) {
$newData = $this->generateBatch($userIds, $type);
$pipeline = $this->redis->pipeline();
foreach ($newData as $i => $data) {
$pipeline->setex($keys[$missIndices[$i]], 3600, $data);
}
$pipeline->exec();
// 合并结果
foreach ($newData as $i => $data) {
$results[$missIndices[$i]] = $data;
}
}
return $results;
}
/**
* 使用hash结构缓存复杂数据结构
*/
public function storeDetailedRecommendation($userId, $type, $recommendations)
{
$hashKey = "rec_detail:{$type}:{$userId}";
$pipeline = $this->redis->pipeline();
// 存储为hash,提高访问性能
foreach ($recommendations as $item) {
$pipeline->hset($hashKey, $item['id'], json_encode($item));
}
$pipeline->expire($hashKey, 3600);
return $pipeline->exec();
}
/**
* 压缩存储
*/
public function compressAndStore($key, $data)
{
$compressed = gzcompress(json_encode($data), 9);
$this->redis->setex($key, 3600, $compressed);
}
public function getAndDecompress($key)
{
$compressed = $this->redis->get($key);
if ($compressed) {
return json_decode(gzuncompress($compressed), true);
}
return null;
}
}
使用示例
<?php
// 使用示例
class RecommendationController
{
private $cacheService;
public function getRecommendations($userId)
{
$cache = new MultiLevelCache();
// 构建缓存键
$cacheKey = "user:{$userId}:recommendations:1";
// 获取数据(自动多级查找)
$data = $cache->getData($cacheKey);
if (!$data) {
// 如果缓存未命中,进行数据查询
$data = $this->generateRecommendations($userId);
// 写入各级缓存
$cache->setData($cacheKey, $data);
}
return $data;
}
/**
* 定时预热热门推荐
*/
public function prewarmHotRecommendations()
{
$hotUsers = $this->getHotUsers(100); // 获取热门用户
$cache = new CacheProtection();
foreach ($hotUsers as $userId) {
$cache->warmUp("rec:{$userId}");
}
return count($hotUsers);
}
/**
* 用户行为触发更新
*/
public function onUserAction($userId, $action, $itemId)
{
// 清除相关缓存
$cacheManager = new CacheManager();
$cacheManager->invalidateUserCache($userId);
// 异步更新
$cacheManager->asyncUpdateCache(
"rec:{$userId}",
function() use ($userId) {
return $this->generateRecommendations($userId);
}
);
}
}
- 合理设置缓存时间:根据数据热度动态调整TTL
- 使用多级缓存:内存→Redis→数据库的降级顺序
- 防止缓存击穿:使用互斥锁保证只有一个请求重建数据
- 防止缓存穿透:使用布隆过滤器或空值缓存
- 防止缓存雪崩:随机化TTL,错开过期时间
- 及时更新:用户行为或其他数据变更时主动失效
- 监控告警:监控缓存命中率、延迟等指标
- 序列化选择:优先使用MessagePack或Protocol Buffers替代JSON,减少存储空间和序列化开销
这样可以构建一个高性能、稳定的推荐结果缓存系统。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播