一、为什么需要缓存
在日常高并发的业务场景中,缓存是我们必不可少的一个手段。我们通常使用缓存手段来将一些热点或者不变的数据缓存起来,减少对后端服务的压力。然而,缓存的选型也是一门学问,如何正确选择缓存的手段,以及缓存的方式,也是一门学问。
二、主动缓存与被动缓存
主动缓存一般是通过定时任务或者脚本,在用户访问之前,将预先的一些数据放到缓存去,使得用户访问的永远是缓存中的数据。
被动缓存指当用户第一次访问时,缓存中没有该份数据,用户的访问触发缓存,在该缓存的有效期内,后续用户访问相同内容,都是从缓存中取数据。
三、缓存可能导致的问题
从上面两种不同的访问方式来看,有经验的人就能知道两种方式中各自存在的问题,比如被动缓存,如果在第一次访问的过程中,很多请求同时打过来,此时缓存中没有相应的数据,那么对后端服务造成的压力是不可估计的。这也就衍生出来缓存的三种不同的问题。
1. 缓存雪崩:指大量的缓存在同一时刻同时失效,导致缓存中没有数据的情况。
2.缓存穿透:指请求的数据是后端服务中也没有的数据,因此不管请求多少次,缓存中都不会有这个数据,从而导致缓存失效的情况
3.缓存击穿:指缓存中的数据因为有效期失效,而同时又有大量的请求过来,从而给后端服务带来较大压力的情况。
简而言之:缓存雪崩时大量缓存同时失效,缓存击穿是后端服务有数据但是内存中没有,或者内存中的数据刚好到期的时候而该缓存内容的请求量又比较大的情况。缓存穿透更多的是一种攻击的模式,请求方恶意请求数据库中没有的数据,因为没有数据,所以导致缓存中没有缓存数据,导致缓存失效。
四、针对缓存的情况我们可以怎样进行操作:
被动缓存需要考虑的点:
由于被动缓存存在缓存击穿的情况,因此在选择被动缓存的时候,思路如下:
请求过来的时候,当有缓存的时候,返回缓存的数据,如果没有缓存,则从数据库中获取数据,然后将数据存到缓存中去;
导致的问题:缓存击穿的情况还是在诶?要是有大量的请求在你还没有缓存的时候,那怎么办?
升级版1.0:请求过来的时候,如果缓存的有效时间已经到了,用户返回旧数据,然后触发一个线程去更新缓存;
升级版1.0 有一个问题,虽然在缓存失效的时候,用户请求得到了及时的响应,但是有大量请求的时候,还是可能会导致多个线程同时去更新缓存,对后端的压力还是不可避免,因此,可以加个并发锁,保证对于同一个缓存,在同一时刻,只有一个线程去更新对应的缓存,因此:
升级版1.1: 请求数据并且缓存失效的时候 -〉用户返回旧数据 -〉抢更新缓存的锁,抢到了更新缓存,抢不到就结束流程。
升级版1.1的方式看上去已经挺完美的了,目前使用 Caffein 缓存 就支持该类缓存,在平时的时候,我们用的缓存也可以实现这样的被动缓存策略。
但是,上面的方法是不是就没有缺点了呢?(这里我们主要考虑缓存失效或者缓存中没有数据的情况)
当缓存失效的时候,如果为了保证用户在访问的时候,能快速返回,那需要保存着历史数据,那么,如果数据量比较大的话,那么缓存中势必存在着大量其实已经过期的数据,如果用户没有来请求触发缓存更新的话,那么占用缓存空间的这堆缓存的其实是无效的缓存。
所以一定要考虑
a. 在该应用场景下,是否可以接受旧数据;
b. 是否能接受空数据(如果能接受空数据的话,就可以直接当缓存中没有数据的时候,更新缓存数据,但是用户可以直接返回空,不用等待数据处理完)
c. 是否可以接受阻塞(如果可以接受阻塞的话,那么当用户请求过来的时候,只要负责更新缓存的线程还没有更新完数据,那么其他所有的请求都等待着,这个需要控制阻塞的时间点以及阻塞的队列大小)
d. 后端并发能力的考虑(一般来说,后端并发能力都有限,但是对于用户的请求如果在一定范围内的,是可以直接返回的,所以可以进行下以下的操作:对后端服务进行下熔断限流,对部分用户可以直接打到后端的服务上去)
e. 关于存储的数据类型(对于常用的缓存,如redis,有着相对比较多种的数据类型格式,因此选择合理的数据格式,可以在进行缓存数据的获取的时候,减少不必要的资源浪费和性能损耗,比如使用文件存储,如果将数据都存在一个文件中,而每次都只是需要文件中的一行数据,而在进行缓存读取的时候,每次都必须将整个文件读到内存中;另外,在redis中,如果将数据存成json格式,在进行分页的时候就不好进行操作,而存成list之类的形式,可以进行方便的分页,并且减少了每次从缓存中读取的IO和流量损耗)
五、在使用缓存中,一些常见的指标:
- 缓存命中率 (缓存的命中率指标是评判缓存的设置是否合理的重要可量化指标,像redis的话,缓存的命中率可以通过 info命令,通过 keyspace_hits 和 keyspace_misses 两个字断来看)
- 数据利用率 (从缓存中取的数据,是否就是直接返回给用户的所有的数据呢?如果取了10K数据,但是最终返回给用户的数据只有1K,那么数据的利用率是比较低的,这个时候就要考虑下使用缓存的其他方式了)
网友评论