zoukankan      html  css  js  c++  java
  • Redis在实际开发中面临的问题

    1、缓存雪崩

      缓存雪崩就是Redis 的大量热点数据同时过期(失效),因为设置了相同的过期时间,刚好这个时候Redis请求的并发量又很大,就会导致所有的请求落到数据库。

      缓存雪崩的解决方案:

      (1)加互斥锁或者使用队列,针对同一个key只允许一个线程到数据库查询

      (2)缓存定时预先更新,避免同时失效

      (3)通过加随机数,使key在不同的时间过期

      (4)缓存永不过期或双缓存

    2、缓存穿透

      因为每次查询的值都不存在导致的Redis失效的情况,我们就把它叫做缓存穿透。

               

      缓存穿透分为两种情况:

    (1)多次重复查询相同的值

      这种情况可以缓存空数据或特殊字符,使不存在的数据不必一直查缓存,并发高的情况下可以加分布式锁,避免同一时间点数据库请求打满。

    (2)每次都查询不同的值

      这个问题的关键在于如何判断要查询的值在不在数据库中,也是一个经典问题:如何在海量元素中(例如10亿无序、不定长、不重复)快速判断一个元素是否存在?

    2.1、第一种方案

      使用位图(bitMap),它是一个有序数组,只有0和1,0表示不存在,1表示存在。

        

      要通过有序的位置来标识10亿个元素是否存在,需要将元素值与数组下标做一个映射,通过映射转换成下标的时候,应该在数组中尽可能均匀分布。可以通过哈希函数进行映射,比如MD5、SHA-1等常见的哈希算法。如下6个元素经过哈希算法和位运算得到相

    应的下标:

        

       但是会存在一个问题,就是不同元素得到的哈希值一样,这样就会有相同的下标(如上图的Tom和Mic),这种情况也就哈希碰撞。要避免哈希碰撞,可以通过扩大位图容量或是增加哈希计算次数,但是位图容量太大会产生存储空间过大,哈希计算次数太多消

    耗时间等问题,所以要对二者进行平衡,这就有了第二种方案。

    2.2、第二种方案:基于内存的布隆过滤器

    (1)布隆过滤器工作原理

      首先,布隆过滤器的本质第一种方案提到的一个位数组,和若干个哈希函数。

            

      如上:集合里面有3个元素,要把它存到布隆过滤器里面去,应该怎么做?首先是a元素,这里我们用3次计算。b、c元素也一样。元素已经存进去之后,现在我要来判断一个元素在这个容器里面是否存在,就要使用同样的三个函数进行计算。比如d元素,我用第

    一个函数f1计算,发现这个位置上是1,没问题。第二个位置也是1,第三个位置也是1 。如果经过三次计算得到的下标位置值都是1,这种情况下,能不能确定d 元素一定在这个容器里面呢? 实际上是不能的。比如这张图里面,这三个位置分别是把a,b,c存进去的时

    候置成1的,所以即使d元素之前没有存进去,也会得到三个1,判断返回true。所以,这个是布隆过滤器的一个很重要的特性,因为哈希碰撞不可避免,所以它会存在一定的误判率。

      布隆过滤器的特点:

           1、如果布隆过滤器判断元素在集合中存在,不一定存在

        2、如果布隆过滤器判断不存在,一定不存在

    (2)Guava中布隆过滤器的实现

      谷歌的Guava里面就提供了一个现成的布隆过滤器。 

    <dependency> 
         <groupId>com.google.guava</groupId> 
         <artifactId>guava</artifactId>
         <version>21.0</version> 
    </dependency>

      创建布隆过滤器:

    BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),insertions);

      布隆过滤器提供的存放元素的方法是put(),判断元素是否存在的方法是mightContain()。

      布隆过滤器把误判率默认设置为0.03,也可以在创建的时候指定。

    public static<T> BloomFilter<T> create(Funnel<? super T>funnel,long expectedInsertions){ 
    return create(funnel,expectedInsertions,0.03D);
    }

      位图的容量是基于元素个数和误判率计算出来的:

    long numBits = optimalNumOfBits(expectedInsertions,fpp);

      根据位数组的大小,进一步计算出了哈希函数的个数:

    int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions,numBits);

      存储100万个元素只占用了0.87M的内存,生成了5个哈希函数。

    (3)布隆过滤器在项目中的使用

      布隆过滤器的工作位置:

                              

      因为要判断数据库的值是否存在,所以第一步是加载数据库所有的数据。在去Redis查询之前,先在布隆过滤器查询,如果bf说没有,那数据库肯定没有,也不用去查了。如果bf说有,才走之前的缓存和数据库查询流程。

     (4)布隆过滤器其他应用场景

      布隆过滤器解决的问题是:如何在海量元素中快速判断一个元素是否存在。所以除了解决缓存穿透的问题之外,我们还有很多其他的用途。

      比如爬数据的爬虫,爬过的url我们不需要重复爬,在几十亿的url里面,判断一个url是不是已经爬过了

      还有邮箱服务器,发送垃圾邮件的账号把它们叫做spamer,在这么多的邮箱账号里面,怎么判断一个账号是不是 spamer 等等一些场景,都可以用到布隆过滤器。

     3.3、第三种方案: Redis布隆过滤器

      基于内存的布隆过滤器不适用于分布式,且无法存储大量数据。

    (1)redis布隆过滤器的安装

      下载地址github:https://github.com/RedisBloom/RedisBloom

      然后编译: 

                                

       先把 Redis 给停掉,在 redis.conf 里面添加一行命令->加载模块:

            

    (2)使用

      布隆过滤器添加和查询命令:

            

      添加的lua脚本:

    local bloomName = KEYS[1]
    local value = KEYS[2]
    --bloomFilter
    local result_1 = redis.call('BF.ADD',bloomName,value)
    return result_1

      查询的lua脚本:

    local bloomName = KEYS[1]
    local value = KEYS[2]
    --bloomFilter
    local result_1 = redis.call('BF.EXISTS',bloomName,value)
    return result_1

      

  • 相关阅读:
    Step by Step To Create a K8S Cluster
    Linux简单操作指令
    安装redis 最新版 redis-6.2.6
    GCC升级到11.2.0
    SQL开窗函数
    SQL 树形结构递归查询
    一文详解python的类方法,普通方法和静态方法
    _new_()与_init_()的区别
    关于Python的import机制原理
    【原创】android内存管理-hprof文件
  • 原文地址:https://www.cnblogs.com/jing-yi/p/12933795.html
Copyright © 2011-2022 走看看