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

  

原文地址:https://www.cnblogs.com/jing-yi/p/12933795.html