概述
PHP+Redis解决实际问题:缓存击穿
问题描述:
PHP+Redis如何简单避免缓存击穿
解决方案:
<?php
/**
* 防击穿缓存
* @param string $key 缓存key
* @param int $expire 缓存过期时间(s)
* @param bool $refresh 是否强制刷新数据
* @param callable $func 获取要缓存的数据的回调方法(仅支持返回类型:?string)
* @return null|string 返回缓存的值,没有值时且没有抢到刷新权且缓存已过期时返回null
*/
function onceCache(string $key,int $expire,bool $refresh,callable $func): ?string
{
//内存,注:如果是cli模式不要使用
static $dataStatic=[];
//读取内存
if(isset(self::$dataStatic[$key])){
return self::$dataStatic[$key];
}
//获取laravel的获取redis连接,其它框架需要修改
$redis=Redis::connection();
//原缓存key加后缀"lock"为锁的key
$lockKey=$key.':lock';
//锁是否有效
$lock=false;
//读取数据
$data=$redis->get($key);
//如果 非强制刷新 且 缓存非空 ,获取锁
if(!$refresh && !is_null($data)){
$lock = $redis->get($lockKey);
}
if(!$lock){//如果锁过期或无数据
$lock=$redis->setnx($lockKey,1); //仅当锁不存在时设置锁,值1代表数据获取中
if($lock || $refresh){ //抢到新锁 或 强制刷新
try {
$data=$func(); //从回调函数获取要缓存的数据
//有数据则写入缓存,没有则删除数据
if(!is_null($data)){
$redis->set($key,$data);
}else{
$redis->del([$key]);
}
$redis->set($lockKey,2,'ex',$expire); //设置锁的过期时间,值2代表数据获取完成
}catch (Exception $exception){
$redis->del([$lockKey]); //发生异常,删除锁
}
}else{
//如果没有数据,又没有抢到锁
//30秒内每秒判断抢到锁的用户是否执行完成,执行完成则从缓存得到数据并返回
$retry=0;
do{
sleep(1);
$retry++;
$data = $redis->get($key);
}while(is_null($data) && $redis->get($lockKey)==1 && $retry<30);
}
}
//写入内存
if(!is_null($data)){
$dataStatic[$key]=$data;
}
//返回数据
return $data;
}
登录后复制
代码解读:
数据本体永不过期
设置锁的过期时间,锁过期则抢到锁的用户刷新数据
未抢到锁的用户如果有拿到数据就直接返回(此时数据已经是过期数据,如果对数据及时性要求高的需要自己改造代码)
未抢到锁的用户如果没有拿到数据则每秒判断抢到锁的用户是否执行完成
锁有极小概率变成死锁,最好有定时任务定期处理,比如每天业务低谷期清理死锁
推荐学习:《PHP视频教程》
以上就是PHP+Redis解决缓存击穿的实际问题的详细内容,更多请关注靠谱客其它相关文章!
最后
以上就是舒服豌豆为你收集整理的PHP+Redis解决缓存击穿的实际问题的全部内容,希望文章能够帮你解决PHP+Redis解决缓存击穿的实际问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复