redis缓存穿透及解决方案

redis常用业务场景

 

如何解决缓存穿透带来的问题

如果是多次查询相同不存在的值,可以将该空值也给添加至redis中,并且给失效时间,因为防止后面该条数据会有更新。

问题:如何在海量数据(例如10亿无序,不定长,不重复)快速判断某一元素是否存在?

分析思路:

首先,10亿条数据肯定不能直接去mysql,因为mysql数据是在磁盘上,会很慢。

能否直接放到redis???

也不可以,因为如果全放到redis,就无法保证redis和mysql的数据一致性。

所以我们必须要有个存储的数据的容器,常见的数据结构有:

数组,链表,树,hashMap........

如果一条数据有10kb,那么十亿条数据就有几百G的大小,普通的服务器无法承受。

所以需要找一种最简单的数据结构

布隆过滤器详解

1):数据结构是位数组(二进制向量)

2):一系列随机hash映射函数

布隆过滤器根据key通过多次hash算法计算出位数组的下标,根据下标位置判断出是否存在

布隆过滤器结果具有以下特点:

1)如果布隆过滤器判断元素存在,真实结果不一定存在(因为可能会出现hash碰撞)

2)如果布隆过滤器判断不存在,则元素一定不存在

3)如果元素实际存在,则布隆过滤器一定存在

4)如果元素实际不存在,则布隆过滤器可能存在

手撕代码:统计布隆过滤去命中率

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.text.NumberFormat;
import java.util.*;

public class BloomFilterDemo {
    //模拟一共有100万条数据
    private static final int count = 1000000;

    public static void main(String[] args) {
        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), count);
        //实际存在的key,取出使用
        List<String> list = new ArrayList<String>();
        //实际存在的key,判断是否存在
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < count; i++) {
            String uuid = UUID.randomUUID().toString();
            list.add(uuid);
            set.add(uuid);
            bloomFilter.put(uuid);
        }
        //正确判断的次数
        int right = 0;
        //错误判断的次数
        int error = 0;
        for (int i = 0; i < 10000; i++) {
            String data = i % 100 == 0 ? list.get(i) : UUID.randomUUID().toString();
            if (bloomFilter.mightContain(data)) {
                if (set.contains(data)) {
                    right++;
                    continue;
                }
                error++;
            }
        }
        NumberFormat percentInstance = NumberFormat.getPercentInstance();
        percentInstance.setMinimumFractionDigits(2);
        float wrong = (float) error / 9900;
        float bingo = (float) (9900-wrong) / 9900;
        System.out.println("判断100个元素存在的数据,布隆过滤器认为存在的有:"+right+"\t"
        +"判断9900个元素不存在的数据,布隆过滤器认为不存在的有:"+error+"\t命中率为:"+percentInstance.format(bingo)
                +"误判率为:"+percentInstance.format(wrong));
    }

运行结果:

判断100个元素存在的数据,布隆过滤器认为存在的有:100	判断9900个元素不存在的数据,布隆过滤器认为不存在的有:290	命中率为:100.00%误判率为:2.93%

1:离线数据加载到布隆过滤器

2:布隆过滤器查询

3:过滤器不存在,则直接返回

4:过滤器存在,cache不存在,从数据库查询

5:数据库返回

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于 Redis 缓存的穿透、击穿和雪崩问题,可以采取以下解决方案: 1. 缓存穿透:当请求的数据在缓存中不存在时,会直接访问数据库,如果有恶意攻击者大量请求不存在的数据,会给数据库造成很大压力。解决方案可以是在查询前进行参数校验,比如对请求的参数进行合法性检查,或者使用布隆过滤器等技术来快速判断请求的数据是否存在。 2. 缓存击穿:当某个热点数据过期或被删除时,大量请求同时涌入,导致请求直接访问数据库。为了解决这个问题,可以使用互斥锁(Mutex)或者分布式锁来避免多个请求同时访问数据库。在获取锁之前,首先尝试从缓存获取数据,如果缓存中存在,则直接返回;如果不存在,则获取锁,并从数据库中获取数据并放入缓存,最后释放锁。 3. 缓存雪崩:当缓存中的大量数据同时过期时,会导致大量请求直接访问数据库,给数据库带来巨大压力。为了解决这个问题,可以采取以下措施: - 设置合理的缓存过期时间,使得不同数据的过期时间错开,避免同时失效。 - 使用热点数据预加载,提前将热点数据加载到缓存中,避免同时失效。 - 使用多级缓存架构,将缓存分为多个层级,不同层级的缓存设置不同的过期时间,从而降低缓存失效的风险。 - 引入限流和熔断机制,对请求进行限制,避免大量请求同时访问数据库。 通过以上措施,可以有效地解决 Redis 缓存穿透、击穿和雪崩问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值