1. 什么是缓存穿透,怎么解决?
1.1. 缓存穿透
查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都会查询数据库。
1.2. 解决方案
方案一: 缓存空数据
缓存空数据,并设置key过期时间。查询返回的数据为空,仍把这个结果为空进行缓存。
【例:{key:1,value:null}】
优点:简单
缺点:消耗内存,会发生不一致的问题。
方案二:设置布隆过滤器。
优点:内寸占用较少,没有多余key
缺点:实现复杂,存在误判。
2.什么是布隆过滤器?
是一种用于检测集合中元素的数据结构,它可以有效地判断一个元素是否可能在一个集合中,或者判断一个元素是否一定不在集合中。它是一个大小固定的位数组和一系列随机映射函数。
Redis提供了布隆过滤器的支持,可以通过Redis模块(如RedisBloom)或者Redis的集合实现类似功能。
代码实现:
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedisBloomFilterExample {
public static void main(String[] args) {
// 1. 配置RedissonClient
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
// 2. 创建布隆过滤器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("myBloomFilter");
// 初始化布隆过滤器,设置期望元素个数和错误率
bloomFilter.tryInit(10000, 0.01);
// 3. 添加元素
bloomFilter.add("element1");
// 4. 检查元素是否可能存在
boolean mightContain = bloomFilter.contains("element1"); // 应该返回true
boolean mightNotContain = bloomFilter.contains("element2"); // 可能返回false,如果确定不存在,那么一定不存在
// 5. 关闭Redisson客户端
redisson.shutdown();
}
}
3. 什么是缓存击穿,怎么解决?
给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对这个key有大量的并发请求进来,这些并发请求可能瞬间把DB压垮。
3.1 解决方案
方案一 互斥锁
特点:强一致,性能差。
流程:当缓存过期时,不立即去查询数据库,而是先设置一个互斥锁,然后进行数据查询,查询成功后并设置缓存。
在互斥锁的时候,新的数据请求处于等待中。
方案二 逻辑过期
特点: 高可用,性能好,不保证数据绝对一致
流程:
1) 当设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间。
2) 当查询的时候,从redis获取数据后判断时间是否过期。
3) 如果过期,则开通两外一个线程进行数据同步,当前线程返回过期数据。
3.2 总结
如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等待,会产生死锁。
如果选择key的逻辑删除,则优先考虑的高可用性,性能较高,做不到数据的绝对一致性。
设置热点数据永不过期,尽量避免热点数据产生的缓存击穿。
4.什么是缓存雪崩,怎么解决?
同一时间有大量的key过期或者时redis缓存服务器宕机,导致大量的请求进入数据库,导致数据库过大压力。
4.1解决方案
1) 给不同的key添加TTL随机值【在原有的过期时间上,在添加一个随机过期时间】。
2) 利用redis集群提高服务的高可用性。
3) 给缓存服务添加降级限流策略【可用于系统的保底策略。适用于缓存穿透、缓存击穿、缓存雪崩】
4) 给业务添加多级缓存。