摘要:缓存雪崩发生原因主机挂了,缓存全部失效。缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。解决思路布隆过滤器布隆过滤器布隆过滤器英语是年由布隆提出的。
(1) Redis主机挂了,Redis缓存全部失效。
(2) 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
(1) Redis实现高可用。
(2) 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
(3) 限流&降级。
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
@Servicepublic class StudentService { @Autowired private RedisTemplate<String, String> redisTemplate; @Autowired private StudentMapper studentMapper; private final Object lock = new Object(); public Student getStudent(Long id) { //读取redis,存在直接返回 String s = redisTemplate.opsForValue().get(String.valueOf(id)); if (StringUtils.isNotBlank(s)) { return JSON.parseObject(s, Student.class); } //并行变成串行 synchronized (lock) { //读取redis,存在直接返回 String str = redisTemplate.opsForValue().get(String.valueOf(id)); if (StringUtils.isNotBlank(str)) { return JSON.parseObject(str, Student.class); } //从数据库读取存入redis中 Student student = studentMapper.findOne(id); redisTemplate.opsForValue().set(String.valueOf(id), JSON.toJSONString(student), 7, TimeUnit.DAYS); return student; } }}
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
布隆过滤器
布隆过滤器(英语:Bloom Filter)是 1970 年由布隆提出的。
它实际上是一个很长的二进制数组+一系列随机hash算法映射函数,主要用于判断一个元素是否在集合中。
通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。
链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。
但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logn),O(1)。这个时候,布隆过滤器(Bloom Filter)就应运而生
当我们向布隆过滤器中添加数据时,为了尽量地址不冲突,会使用多个 hash 函数对 key 进行运算,算得一个下标索引值,然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
向布隆过滤器查询某个key是否存在时,先把这个 key 通过相同的多个 hash 函数进行运算,查看对应的位置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在;如果这几个位置全都是 1,那么说明极有可能存在;
优点: 高效地插入和查询,占用空间少
缺点: 不能删除元素。因为删掉元素会导致误判率增加,因为hash冲突同一个位置可能存的东西是多个共有的,你删除一个元素的同时可能也把其它的删除了。
存在误判,不同的数据可能出来相同的hash值。
<!--guava Google 开源的 Guava 中自带的布隆过滤器--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency>
public class GuavaBloomFilterTest { /** * 布隆过滤器预计插入多少数据 100w */ private static final int SIZE = 100 * 10000; /** * 误判率 */ private static final double fpp = 0.01; @Test public void mainTest() { //创建布隆过滤器 BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), SIZE, fpp); //100w 存 for (int i = 1; i <= SIZE; i++) { filter.put(i); } //判断100W的数据是否都在布隆过滤器 List<Integer> list1 = new ArrayList<>(SIZE); for (int i = 1; i <= SIZE; i++) { if (filter.mightContain(i)) { list1.add(i); } } System.out.println("存在的数量:" + list1.size()); //取10W个不在布隆过滤器里面的值,判断误判 List<Integer> list2 = new ArrayList<>(SIZE); for (int i = SIZE + 1; i <= SIZE + 1 + 100000; i++) { if (filter.mightContain(i)) { list2.add(i); } } System.out.println("误判的数量:" + list2.size()); }}
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/123659.html
摘要:在某些查询中,可以将所有可能的查询条件放入这个集合,在查询之前使用这个集合对查询条件进行过滤,就可以避免缓存穿透的问题。解决方案二级缓存对于那些热度高的数据设置二级缓存,并且错开和一级缓存的失效时间,使请求不会同时穿透两层缓存去访问数据库 在我们的实际开发应用中,缓存机制的广泛存在,大大的提高了系统对数据库的请求承受阈值,但是在一些特定的场景下,需要去了解它可能出现的问题和对应的解决方...
摘要:在某些查询中,可以将所有可能的查询条件放入这个集合,在查询之前使用这个集合对查询条件进行过滤,就可以避免缓存穿透的问题。解决方案二级缓存对于那些热度高的数据设置二级缓存,并且错开和一级缓存的失效时间,使请求不会同时穿透两层缓存去访问数据库 在我们的实际开发应用中,缓存机制的广泛存在,大大的提高了系统对数据库的请求承受阈值,但是在一些特定的场景下,需要去了解它可能出现的问题和对应的解决方...
摘要:解决方案通过布隆过滤器拦截。对空结果进行缓存,但是过期时间很短,不超过分钟。缓存雪崩介绍缓存雪崩是指设置缓存采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到,瞬间压力过重雪崩。 缓存穿透 介绍 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写,并且处于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓...
摘要:不过,布隆过滤器有一个最大的缺点,也是其为了高效利用内存而付出的代价,就是无法确保的准确率。不过这种方式的优势是前面提到的,不会出现误差,而布隆过滤器的错误率会随着位数的增加而减少,会不断趋近于,但不会为。 如果第二次看到我的文章,欢迎文末扫码订阅我个人的公众号(跨界架构师)哟~ 本文长度为2805字,建议阅读8分钟。坚持原创,每一篇都是用心之作~ 有句话说得好,欲要使其毁灭,先要...
阅读 1366·2021-11-18 10:02
阅读 2257·2021-10-13 09:40
阅读 1023·2021-10-13 09:39
阅读 1137·2021-09-23 11:22
阅读 1628·2021-09-09 09:33
阅读 2116·2019-08-30 14:05
阅读 886·2019-08-29 17:03
阅读 591·2019-08-29 16:24