php+Redis解决实际问题:缓存击穿

问题描述:
PHP+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;
}
代码解读:
数据本体永不过期
设置锁的过期时间,锁过期则抢到锁的用户刷新数据
未抢到锁的用户如果有拿到数据就直接返回(此时数据已经是过期数据,如果对数据及时性要求高的需要自己改造代码)
未抢到锁的用户如果没有拿到数据则每秒判断抢到锁的用户是否执行完成
锁有极小概率变成死锁,最好有定时任务定期处理,比如每天业务低谷期清理死锁