遇到一个没有进行任何优化的WEB项目,需要优化,第一时间想到的就是缓存,先琢磨琢磨哪里可以添加缓存,缓存可以很直接的提升查询的效率,但是使用缓存也有一些绕不开的问题,这次我们就聊聊这些问题。
缓存
先从简单的说起,就是缓存的定义和作用,聊缓存的定义和作用就不得不先聊一下查询的过程,以前后端分离模式来说:
(1)用户访问页面
(2)页面请求后端接口
(3)后端接口查询数据库返回数据
(4)页面接受数据渲染返回给用户
从这个角度上看,没有啥问题,但是老话说的好,抛开数量谈功能,就是耍流氓,拿类似百度的搜索引擎来说,刚刚过去的五一节有很多热点,这些热点基于百度的访问量,每秒钟有成千上万的访问量,那么也就代表着有成千上万的查询,这么搞,如果每次的查询数据库,那么数据库的压力很大啊。所以就需要把频繁查询的内容放到一个容易访问的地方,可以是内存,文件甚至是一个新的数据库,目的就是一个,方便访问,那么接下来这些查询的结果就会直接从存放的位置当中出,而不是再次查询数据库,这种策略就是缓存策略,这种存储就是缓存。
了解了缓存,那么从它的功能和实现方式上,我们会遇到下面的几个问题:
缓存雪崩
缓存放好了,但是如果数据更新了,缓存数据没有更新会导致访问的数据过时,所以,设置缓存会有一个过期时间,缓存没有问题,但是需要有过期时间,过期的缓存将被舍弃,然后重新生成新数据的缓存。但是这个时候就会遇到一个小问题,如果在同一个时间节点上,缓存的所有内容都过期了,那么数据库的压力是不是突然增大N倍,这个就是缓存雪崩。
有了问题,自然需要思考解决的方案:
1、在缓存存储层面,雪崩除了缓存同时到期导致还可能是缓存服务器宕机导致的,比如,redis服务器挂了(嘿嘿嘿,很酸爽),所以在设置缓存之处,有条件的前提下,可以尝试使用集群或者分布式缓存。
2、既然是集体过期,那么就从缓存的过期时间着手,设置为随机值或者阶梯值,保证不会同时过期,或者狠一点,直接永不过期(永不过期有风险,使用要谨慎)。
3、成本高一点的可以考虑异步重建缓存,在服务端层面轮训缓存的过期时间,在过期前异步重建缓存。
缓存穿透
查询一条数据,发现缓存没有存,查询数据库,数据库也没有,但是这个查询的频率还很高,那么自然对服务器的性能产生很大的影响,并且缓存没有达到效果,那么这个就是缓存穿透。
同样,通过问题思考解决方案:
1、最直接的方法,对查询不到的结果缓存一个空值,第一次查询,使用到数据库返回了一个空值,也存入缓存,那么下次缓存直接返回空,但是如果这样的查询很多,容易导致缓存污染(就是空值的缓存占满缓存空间,把真正缓存的内容挤出缓存),所以需要考虑这个查询是否频繁。
2、在缓存的基础上使用布隆过滤器,查询之前先通过布隆过滤器看看数据是否存在,如果返回不存在那么就不要查询了(布隆过滤器可以判断不存在,但是不能确定一定存在,所以使用的时候要以判断不存在的思路使用)。
缓存击穿
缓存击穿和雪崩类似,但是它的问题是基于一个热点的缓存,这个热点访问量很大,但是不巧,这个时候缓存过期了,所以导致,大量的访问需要查询数据库,服务器的压力突然变大(然后,然后,~~哇~~~,它死了~)
解决的方案一般有两种:
1、最直接,缓存永不过期,但是这样数据更新之后,容易导致数据不一致。
2、异步重建或者分布式互斥锁(就是基于一个请求重写创建缓存,其他请求只是获取缓存内容),但是这种方式容易导致高并发的性能突然降低,不过数据一致性保持的还是不错的。
两害取其轻,这里就需要根据业务逻辑实际的抉择了。
思考
与其说上面的缓存问题是问题,其实不如说是缓存在设置过程当中需要注意的点,缓存虽好,还是需要妥善的使用的,最后还是请各位大佬多多指点。