Redis中几个“看似”高大上的概念,经常有人提到,某些好事者喜欢死扣概念,实战没多少,嘴巴里冒出来的全是高大上的名词,个人一向鄙视概念党,呵呵,尼玛!
其实这几个概念:缓存穿透/缓存击穿/缓存雪崩,有一个共通的相似之处,就是高并发下,某些原因导致缓存层失去了保护,导致后端的持久化层(数据库)承担较大压力的情形。
需要注意的是,这些问题发生的前提,需要有足够大的并发性,如果本身并发性不高,那些即便出现了这些个问题,也不会造成非常大的影响。
甚至极端地讲,只要代码的健壮性足够,即便是缓存层全部宕机,也不会导致整个应用程序的崩溃,只不过是所有的请求都指向后端的持久化数据库层罢了。
试问,排出一些低级的问题包括方案,君见过多少请求压垮数据库的场景,或者数据缓存流行之前应用程序就应付不了高并发?
使用缓存,更多的进一步改善性能或者并发,而不是因为数据库被压垮了才使用缓存。
缓存穿透
缓存穿透本质上是查询一个缓存和数据库中都不存在的key值,Redis的缓存层无法命中,导致请求再次指向执行数据库层的情况。
由于是查询到值不存在(当然也不存在与Redis缓存中),导致请求总是“穿透”Redis发往数据库层,因此缓存层失去了“保护”关系数据库层的意义。
解决方案:
布隆过滤器是一个比较好的选择,参考:https://www.cnblogs.com/wy123/p/11571215.html
缓存击穿
缓存穿透本质上是高并发地请求一个缓存中不存在,但是数据库中可能存在的key值,因此请求指向数据库,导致数据库端承担大量的请求压力的情况。
解决方案:
这种场景可以从代码逻辑层面优化,从缓存中查询不到数据,再次将请求转向数据库中的时候,锁定该key,获取到该key之后,将该key写入缓存
value = get_value_from_redis(key1)
if not value:
if exclusiveness_lock(key1):#成功排他性锁定目标,请求指向数据库
value = get_value_from_mysql(key1)
if value:
write_key_to_redis(key1,value)
else:#无法获取排他性锁,间隔0.1s之后再次查询缓存
time.sleep(0.1)
# 再次从缓存中查询
get_value_from_redis(key1)
缓存雪崩
某些原因,比如:
1,Redis实例(主从,集群,哨兵等等)故障。
2,Redis中的key由于过期时间已到,自动过期。
3,由于Redis内存策略导致(maxmemory,maxmemory-policy配置)某些key失效(过期,被清理出缓存等)。
如果此时出现高并发的请求出现,这些请求会全部指向数据库层,缓存层失去了对数据库层的保护,导致数据库承担绝大压力的情况。
解决方案:
1,Redis层的高可用,保证缓存层可以有效地保护数据库层
2,从Redis配置(内存管理策略maxmemory,maxmemory-policy)以及结合业务,避免某些潜在的热点key值过期
3,应用程序端限流,或者通过队列的方式等削峰的方式来保护后端数据库