缓存穿透
概念
查询一个不存在的数据,缓存未查到之后就去数据库查询;查不到数据就不写入缓存,导致每次查询这个数据都会从数据库查。
后果
如果有些不怀好意的人,利用这个不存在的数据,频繁大量的访问你的数据库,产生大量的请求,极有可能导致你的数据库异常访问不了、数据库宕掉。
解决方案
方案一
- 缓存空对象:
- 缓存空对象导致的问题:
1、空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
2、缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5 分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。
- 伪代码:
String get(String key){
// 从缓存中获取数据
String cacheValue = cache.get(key);
// 缓存为空
if(StringUtils.isBlank()){
// 从存储中获取
String storageValue = storage.get(key);
// 如果存储数据为空,需要设置一个过期时间
if(storageValue == null){
cache.expire(key,60);
}
return storageValue;
}else{
// 直接返回缓存中的值
return cacheValue;
}
}
方案二
- 布隆过滤器:
是一个很长的二进制向量和一系列随机映射函数。
- 原理:
存入过程:
通过K个哈希函数计算该数据,返回K个计算出的hash值;
这些K个hash值映射到对应的K个二进制的数组下标;
将K个下标对应的二进制数据改成1。
查询过程:
通过K个哈希函数计算该数据,对应计算出的K个hash值;
通过hash值找到对应的二进制的数组下标;
判断:如果存在一处位置的二进制数据是0,那么该数据不存在。如果都是1,该数据存在集合中。
删除过程:
一般不能删除布隆过滤器里的数据。
- 优点:
由于存储的是二进制数据,所以占用的空间很小;
插入和查询速度是非常快的,时间复杂度是O(K);
保密性很好,因为本身不存储任何原始数据,只有二进制数据。
- 缺点:
(1)存在误判:
添加数据是通过计算数据的hash值,那么很有可能存在这种情况:两个不同的数据计算得到相同的hash值。
(2)删除困难:
如果两个数据的hash值一样,删除的时候会将两个数据都删除掉。
缓存击穿
概念
当某个热点数据在缓存中突然失效.导致大量的用户直接访问数据库.导致并发压力过高造成异常。
后果
造成某个时刻所有访问直接打在数据库上,导致数据库压力剧增。
解决方案
方案一:
设置为永不过期。
方案二:
使用互斥锁。如果缓存失效的情况,只有拿到锁才可以查询数据库,降低了在同一时刻打在数据库上的请求,防止数据库打死。当然这样会导致系统的性能变差。
缓存雪崩
概念
大量数据key失效,导致大量数据打到数据库。
原因
redis宕机;设置了相同的过期时间。
后果
导致缓存、数据库等一系列服务宕机
解决方案
在缓存时给过期时间加一个随机值;避免了因为采用相同的过期时间导致的缓存雪崩.