Redis基于布隆过滤器解决缓存穿透问题(15)

Redis基于布隆过滤器解决缓存穿透问题

1.布隆过滤器基本介绍

布隆过滤器适用于判断某个数据是否在集合中存在,可能存在一定的误判, Bloom Filter基本实现原理采用位数组与联合函数一起实现;实现的原理采用二进制向量数组和随机映射hash函数。

布隆过滤器为什么会产生冲突 ,会根据key计算hash值,可能与布隆过滤器中存放的元素hash产生冲突都是为1,布隆可能会产生误判可能存在。

如何解决这个问题,可以将二进制数组长度设置比较大,可以减少布隆误判的概率。
【适合的场景】

  1. 防止缓存穿透直接访问数据库
  2. 判断用户是否阅读过某一个视频或文章;类似抖音,快手,可能误判,但是不会看到重复的内容
  3. 做黑名单过滤,针对不同的用户是否存在白名单和黑名单,可能误判,但一定程度可以解决问题等等

2.布隆过滤器的优缺点

优点

  1. 支持海量数据场景下高效的判断元素是否存在

  2. 空间占用极小,因为本身不存储数据而是用比特位表示数据是否存在,某种程度有保密的效果。

缺点

  1. 不存储数据本身,所以只能添加但不可以删除,因为删除会导致误判率增加

  2. 由于存在hash碰撞,不一定百分百判断准确,存在一定误差

3.布隆过滤器的原理

布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点(offset),把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。

简单来说就是准备一个长度为 m 的位数组并初始化所有元素为 0,用 k 个散列函数对元素进行 k 次散列运算跟 len(m)取余得到 k 个位置并将 m 中对应位置设置为 1。

4.缓存穿透问题

缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询,使得数据库的压力非常大,甚至导致数据库服务卡死;

应对的方案:

  1. 对我们的服务接口api实现限流、用户授权、黑名单和白名单拦截;
  2. 从缓存和数据库都查询不到结果的话,将数据库空值结果缓存到Redis中;设置合理过期时间(如:5s)避免使用同一个id对数据库攻击。
    如果黑客真的在攻击的情况下,随机成id肯定是不一样的,可采用布隆过滤器。
  3. 布隆过滤器
xml 复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>
java 复制代码
public class BlongTest {
    /**
     * 在布隆中存放100万条数据
     */
    private static Integer size = 1000000;

    public static void main(String[] args) {
        BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
        for (int i = 0; i < size; i++) {
            integerBloomFilter.put(i);
        }
        // 从布隆中查询数据是否存在
        ArrayList<Integer> strings = new ArrayList<>();
        for (int j = size; j < size + 10000; j++) {
            if (integerBloomFilter.mightContain(j)) {
                strings.add(j);
            }
        }
        System.out.println("误判数量:" + strings.size());
    }
}

5.解决Redis缓存穿透问题

java 复制代码
public static BloomFilter<Integer> integerBloomFilter = null;

@RequestMapping("/getOrder")
public OrderEntity getOrder(Integer orderId) {
    if (integerBloomFilter != null) {
        if (!integerBloomFilter.mightContain(orderId)) {
            System.out.println("从布隆过滤器中检测到该key不存在");
            return null;
        }
    }

    // 1.先查询Redis中数据是否存在
    OrderEntity orderRedisEntity = (OrderEntity) redisTemplateUtils.getObject(orderId + "");
    if (orderRedisEntity != null) {
        System.out.println("直接从Redis中返回数据");
        return orderRedisEntity;
    }
    // 2. 查询数据库的内容
    System.out.println("从DB查询数据");
    OrderEntity orderDBEntity = orderMapper.getOrderById(orderId);
    if (orderDBEntity != null) {
        System.out.println("将Db数据放入到Redis中");
        redisTemplateUtils.setObject(orderId + "", orderDBEntity);
    }
    return orderDBEntity;
}

@RequestMapping("/dbToBulong")
public String dbToBulong() {
    List<Integer> orderIds = orderMapper.getOrderIds();
    integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01);
    for (int i = 0; i < orderIds.size(); i++) {
        integerBloomFilter.put(orderIds.get(i));
    }
    return "success";
}

计算布隆过滤器在线网址:布隆过滤计算器

Guava 提供的布隆过滤器的实现还是很不错的,但是它有一个重大的缺陷就是只能单机使用,而现在互联网一般都是分布式的场景。为了解决这个问题就需要用到Redis中的布隆过滤器。

参考:详解布隆过滤器的原理和实现

相关推荐
Dlwyz26 分钟前
redis-击穿、穿透、雪崩
数据库·redis·缓存
工业甲酰苯胺2 小时前
Redis性能优化的18招
数据库·redis·性能优化
Oak Zhang5 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
门牙咬脆骨6 小时前
【Redis】redis缓存击穿,缓存雪崩,缓存穿透
数据库·redis·缓存
门牙咬脆骨6 小时前
【Redis】GEO数据结构
数据库·redis·缓存
墨鸦_Cormorant8 小时前
使用docker快速部署Nginx、Redis、MySQL、Tomcat以及制作镜像
redis·nginx·docker
惜.己9 小时前
Jmeter中的断言(二)
测试工具·jmeter·1024程序员节
Dlwyz10 小时前
问题: redis-高并发场景下如何保证缓存数据与数据库的最终一致性
数据库·redis·缓存
飞升不如收破烂~11 小时前
redis的List底层数据结构 分别什么时候使用双向链表(Doubly Linked List)和压缩列表(ZipList)
redis
吴半杯13 小时前
Redis-monitor安装与配置
数据库·redis·缓存