php解决redis常规三大问题:击穿、雪崩、穿透。

前言:首先说下这三个问题的情况。

一、击穿:指某一时刻突然大量并发下,同时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来避免穿透。

猜你喜欢

转载自blog.csdn.net/weixin_47723549/article/details/128019172