全部
常见问题
产品动态
精选推荐

浅谈Redis缓存的穿透、击穿和雪崩

管理 管理 编辑 删除

Redis是一种流行的内存缓存解决方案,但在实际使用中,会遇到一些缓存相关的问题,其中最主要的就是缓存穿透,击穿和雪崩问题。

一、缓存穿透

缓存穿透是指当查询一个不存在的缓存时,由于缓存中没有相应的数据,而数据库中也不存在对应的数据,导致每次查询都必须访问数据库,这会导致数据库的负载过高。攻击者可以利用这个漏洞进行恶意攻击,通过不断查询不存在的缓存,来使缓存服务器或者数据库瘫痪。

关于缓存穿透常用的的有两种解决办法:

第一种,缓存空数据,当查询一个不存在的数据时,可以将这个查询结果缓存起来,以空对象的形式存储在缓存中。这样,在下一次查询时,如果缓存中存在这个空对象,就可以直接返回,而不会再次查询数据库或接口。

第二种,使用布隆过滤器

<?php

/**
 * 布隆过滤器类
 */
class BloomFilter {
    private $redis; // Redis对象
    private $bitmapSize; // 位图大小
    private $hashCount; // 哈希函数数量

    /**
     * 构造函数
     * @param int $bitmapSize 位图大小,默认为1024
     * @param int $hashCount 哈希函数数量,默认为3
     */
    public function __construct($bitmapSize = 1024, $hashCount = 3) {
        // 实例化Redis对象并连接到Redis服务器
		$redis = new Redis();
		$redis->connect('127.0.0.1', 6379);
		$this->redis = $redis;
        $this->bitmapSize = $bitmapSize;
        $this->hashCount = $hashCount;
    }

    /**
     * 插入元素到布隆过滤器中
     * @param string $element 要插入的元素
     */
    public function insert($element) {
        // 对元素进行多次哈希,并在位图中设置相应位置为1
        for ($i = 1; $i <= $this->hashCount; $i++) {
            $hash = md5($element . $i);
            $position = hexdec(substr($hash, 0, 6)) % $this->bitmapSize;
            $this->redis->setBit('bloom_filter', $position, 1);
        }
    }

    /**
     * 判断元素是否存在于布隆过滤器中
     * @param string $element 要判断的元素
     * @return bool 如果存在返回true,否则返回false
     */
    public function contains($element) {
        // 对元素进行多次哈希,并检查位图相应位置是否为1
        for ($i = 1; $i <= $this->hashCount; $i++) {
            $hash = md5($element . $i);
            $position = hexdec(substr($hash, 0, 6)) % $this->bitmapSize;
            if (!$this->redis->getBit('bloom_filter', $position)) {
                // 如果任意一个位置为0,则表示元素不存在于布隆过滤器中
                return false;
            }
        }
        // 如果所有位置都为1,则表示元素可能存在于布隆过滤器中
        return true;
    }
}

// 缓存穿透解决方案
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// 实例化布隆过滤器对象
$bloomFilter = new BloomFilter($redis);
$cacheKey = 'product_info_123';
if ($bloomFilter->contains($cacheKey)) {
    $productInfo = $redis->get($cacheKey);
    if ($productInfo) {
        return $productInfo;
    }
} else {
    // 如果布隆过滤器中不存在该键,则直接返回空结果
    return null;
}

// 如果缓存中不存在该键,则从数据库中查询并存入缓存和布隆过滤器中
$productInfo = $database->queryProductInfoById(123);
if (!$productInfo) {
    // 如果数据库中也不存在该记录,则插入一个空记录,防止缓存穿透攻击
    $productInfo = '';
    $redis->set($cacheKey, 60, $productInfo);
} else {
    $redis->set($cacheKey, 60, $productInfo);
    $bloomFilter->insert($cacheKey);
}
return $productInfo;

二、缓存击穿

缓存击穿是指当某个热点数据失效时,大量的并发请求访问这个热点数据,导致所有请求都访问数据库,从而使数据库的负载急剧增加。为了避免这种情况发生,可以将热点数据的缓存时间设置为永不过期,或者设置一个较长的过期时间。

$redis->set($key, $value, -1) 和$redis->set($key, $value) 都可以用来设置缓存数据,并且让缓存永不过期。

三、缓存雪崩

缓存雪崩是指当某一时刻缓存中的大量数据同时失效时,大量的并发请求访问这些失效的数据,导致所有请求都访问数据库,从而使数据库的负载急剧增加。为了避免这个问题,我们可以设置缓存的过期时间随机化,使缓存的失效时间不同,避免大量数据同时失效。此外,我们也可以采用缓存预热的方式,提前将热点数据加载到缓存中,以减少缓存的失效率。

在实际应用中,可以通过设置一个随机数生成器,随机生成一个缓存时间偏移量,然后将缓存时间加上偏移量作为实际缓存时间。例如,在一个缓存时间为60秒的基础上,可以随机生成一个0到10秒的偏移量,然后将缓存时间设置为60秒加上该偏移量,即在60到70秒之间随机。

// 缓存时间
$cache_time = 60;

// 生成随机偏移量
$offset = rand(0, 10);

// 设置缓存时间
$expire_time = $cache_time + $offset;

// 设置缓存
$redis->set($key, $value, $expire_time);



徐斗明 最后编辑于2023-09-13 14:10:07

快捷回复
回复({{post_count}}) {{!is_user ? '我的回复' :'全部回复'}}
回复从新到旧

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}}

作者 管理员 企业

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest==1? '取消推荐': '推荐'}}
{{item.floor}}#
{{item.user_info.title}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
{{item.like_count}}
{{item.showReply ? '取消回复' : '回复'}}
删除
回复
回复

{{itemc.user_info.nickname}}

{{itemc.user_name}}

作者 管理员 企业

回复 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}   {{itemc.ip_address}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回复' : '回复'}}
删除
回复
回复
查看更多
回复
回复
1133
{{like_count}}
{{collect_count}}
添加回复 ({{post_count}})

相关推荐

吴汐 企业
名称后面的二维码可以添加到我的企业微信,请备注好问题

回答

9666

发布

101

经验

75885

快速安全登录

使用微信扫码登录
{{item.label}} {{item.label}} {{item.label}} 板块推荐 常见问题 产品动态 精选推荐 首页头条 首页动态 首页推荐
加精
取 消 确 定
回复
回复
问题:
问题自动获取的帖子内容,不准确时需要手动修改. [获取答案]
答案:
提交
bug 需求 取 消 确 定

微信登录/注册

切换手机号登录

{{ bind_phone ? '绑定手机' : '手机登录'}}

{{codeText}}
切换微信登录/注册
暂不绑定
CRMEB客服

CRMEB咨询热线 咨询热线

400-8888-794

微信扫码咨询

CRMEB开源商城下载 开源下载 CRMEB官方论坛 帮助文档
返回顶部 返回顶部
CRMEB客服