大家好,我是苏三,又跟大家见面了。
前言
缓存在我们日常工作中,经常会使用,但如果用不好坑也挺多的。
这篇文章总结了我工作中使用缓存遇到过的7个坑,还是非常有参考价值得,希望对你会有所帮助。
1 缓存穿透
大部分情况下,加缓存的目的是:为了减轻数据库的压力,提升系统的性能。
一般情况下,如果有用户请求过来,先查缓存,如果缓存中存在数据,则直接返回。
如果缓存中不存在,则再查数据库,如果数据库中存在,则将数据放入缓存,然后返回。如果数据库中也不存在,则直接返回失败。
流程图如下:
但如果出现以下这两种特殊情况,比如:
-
用户请求的id在缓存中不存在。
-
恶意用户伪造不存在的id发起请求。
这样的用户请求导致的结果是:每次从缓存中都查不到数据,而需要查询数据库,同时数据库中也没有查到该数据,也没法放入缓存。
也就是说,每次这个用户请求过来的时候,都要查询一次数据库。
图中标红的箭头表示每次走的路线。
很显然,缓存根本没起作用,好像被穿透了一样,每次都会去访问数据库。
这就是我们所说的:缓存穿透问题。
如果此时穿透了缓存,而直接数据库的请求数量非常多,数据库可能因为扛不住压力而挂掉。呜呜呜。
那么问题来了,如何解决这个问题呢?
1.1 校验参数
我们可以对用户id做检验。
比如你的合法id是15xxxxxx,以15开头的。如果用户传入了16开头的id,比如:16232323,则参数校验失败,直接把相关请求拦截掉。这样可以过滤掉一部分恶意伪造的用户id。
1.2 使用布隆过滤器
如果数据比较少,我们可以把数据库中的数据,全部放到内存的一个map中。
这样能够非常快速的识别,数据在缓存中是否存在。如果存在,则让其访问缓存。如果不存在,则直接拒绝该请求。
但如果数据量太多了,有数千万或者上亿的数据,全都放到内存中,很显然会占用太多的内存空间。
那么,有没有办法减少内存空间呢?
答:这就需要使用布隆过滤器了。
布隆过滤器底层使用bit数组存储数据,该数组中的元素默认值是0。
布隆过滤器第一次初始化的时候,会把数据库中所有已存在的key,经过一些列的hash算法(比如:三次hash算法)计算,每个key都会计算出多个位置,然后把这些位置上的元素值设置成1。
之后,有用户key请求过来的时候,再用相同的hash算法计算位置。
-
如果多个位置中的元素值都是1,则说明该key在数据库中已存在。这时允许继续往后面操作。
-
如果有1个以上的位置上的元素值是0,则说明该key在数据库中不存在。这时可以拒绝该请求,而直接返回。
1.3 缓存空值
上面使用布隆过滤器,虽说可以过滤掉很多不存在的用户id请求。但它除了增加系统的复杂度之外,会带来两个问题:
-
布隆过滤器存在误杀的情况,可能会把少部分正常用户的请求也过滤了。
-
如果用户信息有变化,需要实时同步到布隆过滤器,不然会有问题。
所以,通常情况下,我们很少用布隆过滤器解决缓存穿透问题。其实,还有另外一种更简单的方案,即:缓存空值。
当某个用户id在缓存中查不到,在数据库中也查不到时,也需要将该用户id缓存起来,只不过值是空的。
这样后面的请求,再拿相同的用户id发起请求时,就能从缓存中获取空数据,直接返回了,而无需再去查一次数据库。
优化之后的流程图如下:
关键点是不管从数据库有没有查到数据,都将结果放入缓存中,只是如果没有查到数据,缓存中的值是空的罢了。
最近就业形式比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。
你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。
进群方式,添加苏三的私人微信:su_san_java,备注:CSDN&#