缓存的常见面试题I
摘要
最近知乎上有一个问题是关于缓存的一些知识,问题主要是缓存的穿透、击穿和雪崩等等,这些主要涉及的不是什么高深的知识,只是在系统设计过程中,遇到的一些问题的解决方案,人们用一些词语来形象的概况了下问题的情形,所以才有了上述的问题。
笔者在面试人员的时候,如果能通过了前面的基础面试后,也会来一题关于缓存的问题,下面,笔者就通过平常的面试过程,来给大家聊聊这个缓存的问题。
缓存穿透
首先恭喜候选人通过了前面的基础面试关,下面,我们来设计一个缓存相关的系统,设计这个系统的过程中,我会插入一些问题。
候选人:好的,设计一个系统的缓存的时候,我们需要先把数据库或者其他相较于内存读取速度低的介质中提前读取到内存中,通过内存的读取来加快其他介质读取稍慢的问题,也能显著减少数据库和系统的压力。首先,我们先把一条数据的key及相关的数据value加载到redis类似的缓存服务上,读取时,通过get(key) 来校验是否有当前的数据,如果缓存中没有,我们就再次读取数据库来把数据加载到缓存中。如果数据库中也没有,那就返回一个空值给访问者。
笔者:是的,稍等下,这个地方,我插入一个问题,如果再用这个key来访问我们的系统,您是如何设计哪?
候选人:当一个key不存在我们系统中,那么,再第一次访问的时候,我们就可以把当前的key放到缓存中,并把key设置为一个空值,这样,当再次访问系统时,就可以直接从缓存中读取到当前key为空,就可以直接返回了。(笔者:那么,您是如何区别从缓存中得到的空值,还是没有key而返回的空值?)或者设置一个代表为空的值来进行判断。
笔者:嗯。这是一个解决思路,那么,如果我有大量的数据库中不存在的key来访问,您要如何解决大量数据读取缓存的时候没有命中的问题?您又有多少解决方案来处理?
候选人:如果是大量的不重复不存在的key访问系统,第一个,我们可以对key设置一个规则,如果不符合我们规则的key,系统在读取缓存前就可以过滤掉。第二个,我们可以通过布隆过滤器来实现数据库中没有key的数据的过滤。
笔者:您可以大概介绍下布隆过滤器吗?
候选人:好的,布隆过滤器实际上是一个有多个hash函数的bitmap的计数器,首先初始化数据库中所有数据的key通过多个hash函数计算在bitmap中的位置,不同的hash函数计算出的位置不同,通过多个hash函数计算,尽量避免单一hash函数造成的误差。大概的伪代码实现:
public class BloobFilter {
private Bitmap bitmap ;
private HashFuction[] hashs;
public void init() {
for(Key key : list) {
for(HashFunction hash : hashs) {
int w = hash(key) ;
bitmap.put(key , 1) ;
}
}
}
public boolean exist(Key key) {
boolean flag = true ;
for(HashFunction hash : hashs) {
int w = hash(key) ;;
Integer v = bitmap.get(w) ;
if(v == 0) {
flag = false ;
return false ;
}
}
return flag ;
}
}
bitmap的大概存储与示意如图
笔者:好的,您刚才讲的就是缓存传统的一个情况,以及缓存穿透的解决方案,其中布隆过滤器的一些优劣还可以再深入介绍下。(本文就不在此讨论了。)
小结
当有很多很多不存在的数据来访问系统时,我们需要先进行一步过滤,可以通过规则来过滤,也可以通过上文讲的布隆过滤器来进行过滤,布隆过滤器是存在误差的,只能粗略的过滤下不存在的key,当布隆过滤器查询出来的数据不存在,则数据一定不存在,如果存在,也有可能在系统中不存在。后续的不存在的key还是需要进行处理和查询的。其次,布隆过滤无法删除系统中已经删除的key。
后续
缓存的问题分了三个问题,本文先只处理其第一问,敬请期待后续的其他两个问题的解析。