前言:首先说下这三个问题的情况。
一、击穿:指某一时刻突然大量并发下,同时redis的key正好失效,导致大量请求访问了mysql数据库,也就是所谓的越过了redis直接把压力给了mysql可能导致宕机,这就是击穿。
二、雪崩:指某一时刻突然大量并发下,同时redis大量的key集体失效,导致类似于击穿成倍的压力给了数据库,这就是雪崩。
三、穿透:一般是恶意攻击可能发生,比如查询数据局中没有的值,如:id=-1、99998799,这样不存在的数据。导致越过redis同时穿透了mysql,这就是穿透。
直接上代码:
<?php
namespace app\v1\model;
use app\common\model\Banner;
use filelock\FileLock;
use think\facade\Cache;
class BannerModel
{
public static function selectBanner(){
//查询redis是否存在数据
$rBanner = Cache::store('redis')->get('banner');
if($rBanner){
//存在则获取
$banners = json_decode($rBanner, true);
}else{
//不存在则查询mysql
//php文件排它锁,非阻塞。
$l = new FileLock("banner");
//判断是否拿到锁,防止redis击穿问题。
if($l->lock()){
//查询数据库
$banners = Banner::order('sort desc')->field('image')->select()->toArray();
//查询数据写入redis,采用随机失效时间,防止redis雪崩问题。不判断是否存在数据,无数据也写入redis,防止穿透问题。
Cache::store('redis')->set('banner',json_encode($banners),rand(300, 600));
//解锁并删除文件。
$l->unlock();
}else{
//锁占用返回false,接口返回等待。
return false;
}
}
foreach ($banners as $k=>$v){
$banners[$k]['image'] = request()->domain().$v['image'];
}
return $banners;
}
}
文件锁手写的类:
<?php
namespace filelock;
class FileLock
{
private $fileName = '';
private $fp = null;
/**
* @param $name key名称
*/
public function __construct($name)
{
$this->fileName = $name.'.txt';
$this->fp = fopen($this->fileName, "w");
}
//加锁
public function lock(){
return flock($this->fp,LOCK_EX | LOCK_NB);
}
//解锁
public function unlock(){
flock($this->fp,LOCK_UN);
unlink($this->fileName);
}
}
解决方案:
1、击穿:采用锁形式防止在key失效时大量并发访问mysql,如:第一个拿到锁的去查库写redis,没拿到锁的则访问失败,等解锁后再次访问时拿的便是redis的数据。
2、雪崩:所有写入redis的采用随机失效时间,防止同一时刻集体失效,避免悲剧。
3、穿透:对于mysql查不到的数据也存入缓存,下次查询则直接走redis来避免穿透。
扫描二维码关注公众号,回复:
14685499 查看本文章