摘要

最近知乎上有一个问题是关于缓存的一些知识,问题主要是缓存的穿透、击穿和雪崩等等,这些主要涉及的不是什么高深的知识,只是在系统设计过程中,遇到的一些问题的解决方案,人们用一些词语来形象的概况了下问题的情形,所以才有了上述的问题。

笔者在面试人员的时候,如果能通过了前面的基础面试后,也会来一题关于缓存的问题,下面,笔者就通过平常的面试过程,来给大家聊聊这个缓存的问题。

上文(第一篇文章)介绍了缓存穿透的问题,这个时候,是数据库中没有对应的数据,是无论如何也无法查找到的数据,我们通过前置的过滤器进行无效数据的过滤,在前文中,我们可以通过设置key的规则进行过滤,也可以通过布隆过滤器进行过滤,也在小结中说了下布隆过滤器的一些局限。

今日,笔者再来介绍下一些数据在数据库中有,但在缓存中没有的情况,这个情况下,我们要如何处理。

缓存击穿

前提,由于缓存服务器的内存是有限的,不可能把所有的数据都存放到内存中,虽然我们会选择一个内存比较大的服务器来作为缓存服务器,但硬件的容量毕竟有限,而我们的数据是可能越来越多,越来越大。那么,缓存的数据就要设置一个缓存过期策略,来淘汰一些数据,这个时候,就涉及到了缓存的命中率。当系统命中率低的时候,缓存击穿的问题就来了。

笔者:前面我们说的数据是没有的数据或者说是无效数据的请求,那如果是有效数据来请求,由于缓存的过期策略导致了数据无法命中时,我们需要如何设计与处理。

候选人:前面的时候,缓存最初的设计的时候,就讲过,当我们的key在缓存中没有请求到的时候,就可以再次请求数据库,并把数据加载到缓存中。

笔者:是的,您的方案是没有问题的,假设,有一个热点key突然失效,这个时候,有大量的请求来访问当前key,你要如何处理?

候选人:如果突然有大量的访问来访问一个刚失效的key,这个时候,第一个请求到达后,发现key值失效,这个时候,就会去取数据库中的值,其余的请求到达后,等待第一个请求获取数据库中key数据后再次访问缓存,这个时候,就需要对当前key进行加锁,再等待进入后,如果key还是空,则进入下一逻辑,如果key不为空,则再次去缓存中去获取一次即可。伪代码如下

class CacheGet {
    private Cache cache ; 
    
    public Object getKey(Key key) {
        Object obj = cache.get(key) ; 
        if(obj == null) {
            sync(key) {
                obj = cache.get(key) ;
                if(obj == null) {
                    obj = getKeyFromDatabase(key) ; 
                    cache.put(key , obj) ;
                }
            }
        }
    }
}

笔者:好的,这就是缓存常见问题中的缓存击穿,当一个key失效后,或者缓存中没有时,大量的请求来访问当前key,如果不做同步,那么,当前数据就会被大量多次的重新从数据库中读取写入,造成资源的浪费。

后续

缓存的问题分了三个问题,本文为缓存击穿的现象与解决方案,敬请期待后续的其他问题的解析。

个人博客