主页>技术社区>IT 技术>编程开发>PHP

php解决缓存击穿的问题

eIT.com.cn 2023/3/16 16:49:39 阅读 4 次

打印


缓存击穿是指缓存中没有的数据,而查询非常频繁的数据,导致大量的请求落到了数据库上,因此很容易导致数据库连接数暴增,甚至导致宕机。

下面是 PHP 解决缓存击穿问题的一般解决方案:

// 获取 Key
$key = 'my_key';

// 根据 Key 从 Redis 中获取数据
$data = $redis->get($key);

// 如果数据不存在,尝试从DB中获取数据
if (!$data) {   // 尝试获取缓存锁   $lockKey  = 'my_redis_lock';   $locked   = $redis->set($lockKey, 1, array('nx', 'ex'=>10));
   // 如果获得缓存锁,再从DB中获取   if($locked) {       $data = '查询数据库的结果';
       // 将查询结果写入 Redis 缓存中,并设置永久过期       $redis->setex($key, 0, $data);       // 删除锁       $redis->del($lockKey);   }   // 如果获取锁失败,则说明另一个进程已经重新写入了缓存,使用之前写入的数据即可   else {       // 设置一个默认的过期时间,以避免缓存被永久锁定       $redis->setex($key, 60, 'default value');       // 等待一段时间之后,重新获取缓存       sleep(1);       $data = $redis->get($key);   }
}

该代码逻辑如下:

1.根据指定 Key 从 Redis 中获取数据;

2.如果数据不存在,设置一个缓存互斥锁,防止大量并发访问 DB,再次从 DB/ 获取数据;

3.如果获取到缓存锁,则从 DB 中获取数据,并将数据写入 Redis 中;

4.如果获取不到缓存锁,则说明其他进程已经获取到了缓存锁,等待一段时间后重试,重新获取数据即可。

解决缓存击穿的方法有很多,以上只是其中一种通用的 PHP 解决方法,实际应用中需要结合具体情况选择合适的解决方案。

以下是基于 Redis 分布式锁和补偿任务的代码示例:

// 获取 Key
$key = 'my_key';

// 根据 Key 从 Redis 中获取数据
$data = $redis->get($key);

if (!$data) {   // 尝试获取分布式锁   $lockKey = 'my_redis_lock';   $locked = false; // 是否已经获得锁
   // 尝试获取锁,并最多等待 3 秒钟   for ($i = 0; $i < 3; $i++) {       $locked = $redis->set($lockKey, 1, array('nx', 'ex' => 10));       if ($locked) {           break;       }       sleep(1);   }
   if ($locked) {       // 获取数据库数据       $data = '查询数据库的结果';
       // 将查询结果写入 Redis 缓存中,并设置永久过期       $redis->setex($key, 0, $data);       // 删除锁       $redis->del($lockKey);   } else {       // 使用补偿任务异步处理缓存       $taskData = array(           'key' => $key,       );
       $result = $redis->lpush('my_redis_queue', json_encode($taskData));       if (!$result) {           // 记录错误日志           error_log('Failed to add task to the task queue!');       }
       // 返回默认值,避免使用错误的数据       $data = 'default value';   }
}

// 返回数据
echo $data;

补偿任务相关的代码如下:

// 任务处理函数,将查询结果写入缓存或数据库
function processTask($redis, $taskData) {   $key = $taskData['key'];   $data = '查询数据库的结果';
   // 将查询结果写入 Redis 缓存中,并设置永久过期   $redis->setex($key, 0, $data);
}

// 拉取任务队列中的任务,并处理任务
function processTaskQueue($redis) {   while (true) {       // 从任务队列中阻塞获取任务,最多等待 10 秒钟       $taskJson = $redis->brpop('my_redis_queue', 10);
       if (!$taskJson) {           // 没有获取到任务,则结束循环           break;       }
       // 解析任务数据       $taskData = json_decode($taskJson[1], true);
       // 处理任务       processTask($redis, $taskData);   }
}

// 启动后台任务处理进程
function startTaskProcessor($redis) {   while (true) {       // 处理任务队列中的任务       processTaskQueue($redis);
       // 等待一段时间后再继续处理任务       sleep(1);   }
}

// 在应用程序启动时启动后台任务处理进程
startTaskProcessor($redis);

以上代码示例中,如果获取缓存的进程都无法获得锁并写入缓存,则使用补偿任务异步处理任务,并在后台异步将查询结果写入缓存。补偿任务的处理逻辑与业务逻辑相似,只是将数据的写入缓存操作改为异步操作,并放入任务队列中。后台任务处理进程会不断地从任务队列中获取任务,并进行处理。



文章来源:刘俊涛的博客 欢迎关注公众号、留言、评论,一起学习。


若有帮助到您,欢迎捐赠支持,您的支持是对我坚持最好的肯定(_)






相关内容


热门栏目


特别声明


最新资讯
热讯排行



合作媒体友情链接
生活常识小贴士 软件开发教程 智慧城市生活网 息县通生活服务[移动版] 息县商圈[移动版] 美食菜谱
健康养生 法律知识 科技频道 电影影讯 留学考研学习 星座生肖|解梦说梦




关于我们 | 联系我们 | 合作媒体 | 使用条款 | 隐私权声明 | 版权声明

      Copyright © 2023 eIT.com.cn. All Rights Reserved. 豫ICP备2022012332号