关于缓存的二三事
现在缓存系统应用广泛,设计一个缓存系统,需要考虑以下问题:缓存穿透,缓存击穿和雪崩效应。
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。解决的方式主要就是把无效的数据过滤掉,让有效数据访问。最常见的就是布隆过滤器,把所有可能存在的数据哈希到一个bitmap中,一般会采用多种hash算法来避免哈希碰撞。当访问来临时,一个一定不存在的数据会被这个bitmap拦截。当然也可以采取一些简单粗暴的方式,比如查询一个不存在的key的时候,把这个key的value当成null缓存起来一段时间,再有这个key的查询过来,直接返回null。具体采用哪种方式,看实际情况咯。
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。解决方式通常是将去数据库拿数据并写cache这个步骤加上互斥锁,例如redis的单机锁:
String get(String key){ String value=redis.get(key); if(value == null) { if(redis.setnx(key_mutex, "1")) { value = db.get(key); redis.set(key,value); redis.delete(key_mutex); }else { Thread.sleep(50); get(key); } }
return value; }
处理缓存击穿另一个方式是热点数据的“永不过期”,不设置缓存的过期时间或用另一个线程在缓存快过期的时候,重新构建一下缓存。
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。解决方案通常有:1、将缓存时间加上一个随机值,避免同一时间大批量过期;2、设置热点数据永不过期;3、将热点数据均匀分布在各缓存数据库中。