php redis缓存穿透,redis使用场景之防止缓存穿透

说到缓存穿透,网上有很多的文章,但大多数文章只是简单的几句话概括了一下,并没有做深入的分析,于是触发了我写这片文章,要把缓存穿透说通透说明白了。

缓存穿透

我们先来说一下什么是缓存穿透,缓存穿透指的是用户访问一个不存在的key,于是绕过了缓存,直接请求数据库。当此并发量过大时就容易导致数据库的吞吐率降低,甚至因压力过大而进程挂掉。

89b96bf3f73424653ca24cd1d428c968.png

解决思路

我们用一个特定的值表示不存在,比如"nil",现实当中可以设置的再复杂一点,保证这个值不会被真实的场景用到。

然后每次我们通过key从数据库中请求获得空数据时,把key和"nil"以string形式插入缓存,这样下次我们请求时就可以从缓存中读取了。当然最好能设置一个过期时间,使无用的数据不至于长时间占用内存。

redis的操作命令如下:

SET key nil EX 10

复制代码

下面是改进的流程图

e122f0146374c333535ad65599376eb8.png

那这样是不是就万事大吉了呢?

我们来思考一下这种场景,用户发送100w个不相同的并且不存在数据库的key。这样每次请求后,还是会穿透缓存。

所以设置"nil"值到缓存的方案只适用于应对相同key的情况,对于大量不同的key还是没有办法解决。

那该怎么办呢?

那如果说我们有神秘系统,这个系统包含我们用到的所有的key(缓存中存在的和已过期的key),用户访问后在缓存中查不到,我们再到神秘系统中查找,如果存在这个key,那证明数据库中是有数据的,可以访问数据库,如果不存在这个key,则表示数据库中无数据,可以直接返回。 具体的流程图如下:

451b123430eeef55c36ddc3872742f68.png

那这神秘的系统该怎么做呢?

有的小伙伴可定想到了,我们可以用redis的string类型啊,存成{key:1}的形式。这样做当然可以,但是用string存储,内存开销是非常大的,有点得不偿失啊。

那怎么办呢?这里,我们就可以使用布隆过滤器了。

布隆过滤器

原理

布隆过滤器是什么?我们从先来看一下布隆过滤器的原理图:

5682d8b278b2f28ecd7fd0b4fdc9954a.png

布隆过滤器底层可以认为是一个很大的数组,数组里面每一项为boolean类型,默认为false。

我们把key1插入布隆过滤器中,在插入前会执行f(),g(),h()三个计算,算出对应的hash值(3,7,11),这个hash值对应的是数组中的位置,于是我们把对应的位置设置为true。

我们插入key2,依然通过f(),g(),h()三个函数计算,计算出hash值为(7,13,17),我们继续把对应的位置设置为true。

刚才我们分析了插入,然后我们来看查询。

我们查询key1,通过三个方法计算出hash值为(3,7,11),我们再查看对应的数组中的值都为1,所以证明key1是存在的

我们查询key3,通过三个方法计算,算出hash值为(3,13,14),我们在查看对应数组中的值(1,1,0),不都为1 证明key3是不存在的。

这里有一种特殊的情况,我们存入key4,算出来hash值为(3,13,17),我们查询数组中的对应的值为(1,1,1), 都为1 表明存在,但事实上key4是不存在的。当然这个误差是可控的。

所以我们可以得出结论:

布隆过滤器中存在的不一定存在(误诊的概率很小),不存在的一定不存在。

复制代码

工具

这里推荐一个布隆过滤器的工具

我们输入1千万的数据,错误率在万分之一,发现只要23m的内存,可以说非常的省内存。

b8807017147f5bbfda7747115e141c28.png

实现布隆过滤器

那我们如何实现布隆过滤器呢? 我推荐两种方案:

使用redis的位图和程序的算法来实现。

使用redis的扩展 rebloom

这里要注意一下:rebloom这个扩展只能使用reids4.0以上版本,如果是4.0以下版本,建议使用第一种方案。

我们大体来说以下reboom的使用方法:

安装:

git clone git://github.com/RedisLabsModules/rebloom

cd rebloom

make

redis-server --loadmodule /path/to/rebloom.so

复制代码

使用:

f08b42c5b92a132a02511761649f829c.png

BF.RESERVE largebloom 0.001 10000000 // 设置错误率为万分之一,容量为1千万的,名为"largebloom"的布隆过滤器

BF.ADD largebloom 1 // 把"1"加入布隆过滤器中

BF.EXISTS largebloom 1 // 验证"1"是否存在

BF.EXISTS largebloom 0 // 验证"0"是否存在

方案合并

上面我们讲了一下如何防止缓存穿透,但在访问数据库的时候,会出现另一个风险,叫做缓存击穿。至于如何防止缓存击穿,我们在上一篇文章当中已有相应的说明了。文章地址我放在下面方便小伙伴阅读:

我们现在把缓存穿透与缓存击穿的两个方案合并一下,可以得出预防的具体步骤:

1.在初始化缓存的时候,需要同步生产一个布隆过滤器,所有可能用到的key放入布隆过滤器中(这里要注意,初始化缓存时,范围是热点数据,而初始化布隆过滤器时,范围是所有用的到数据的key)。

每次新增缓存,同样需要把key加入布隆过滤器中

查询的流程如下面的流程图所示:

23de90bfbb22c797e1fff9a9c436e4c7.png

红色表示新增或者修改的流程。新增或修改的地方有两处,其他和之前的流程一样。

1). 没获取到数据的时候增加了一个布隆过滤器验证,当key不存在时,直接返回。

2). 数据库查询操作后,如果有数据,则插入数据,没有数据则插入空值。

总结

我们今天讲了如何防止缓存穿透,主要是两点:

使用布隆过滤器过滤掉不存在的key。

因为布隆过滤器有误诊行为,所以需要使用string类型的存储来防止漏网之鱼。

我们还分析了布隆过滤器的原理以及使用的方法。总结下来记住一句话:“布隆过滤器说有不一定有,说没有那一定没有”。

最后我们把缓存穿透与缓存击穿的预防方法结合了起来,给出了一套总体的方案。

感谢大家的阅读,也欢迎大家在留言区提供宝贵的意见,哪怕是砖也行,谢谢大家。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值