Redis Module 实现布隆过滤器

2,756 阅读5分钟

Redis Module

Redis module 是Redis 4.0 以后支持的新的特性,这里很多国外牛逼的大学和机构提供了很多牛逼的Module 只要编译引入到Redis 中就能轻松的实现我们某些需求的功能。在Redis 官方Module 中有一些我们常见的一些模块,我们在这里就做一个简单的使用。

个人推荐可以尝试的

  • neural-redis 主要是神经网络的机器学,集成到redis 可以做一些机器训练感兴趣的可以尝试
  • RedisSearch 主要支持一些富文本的的搜索
  • RedisBloom 支持分布式环境下的Bloom 过滤器

布隆过滤器

比如我们存放某些数据的id属性,id属性通过K个Hash 函数,计算到bit(位数组)上面,把它们变成1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。这个一般可以使用在新闻推荐和缓存穿透的处理上面,效果很好。

优点和缺点

优点

空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。 另外, 散列函数相互之间没有关系,方便由硬件并行实现。 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

  • 为什么效率会那么高,是因为布隆过滤器牺牲了准备性和删除操作,布隆过滤器存在一定的误判概率。 比如我们查询某个key ,通过K个hash 函数对应的位数组上面映射的都是1 ,但其实我们的数据存储并没有这个数据,所以存在一定误判。如果我们用来做一个ip 黑名单的添加,可能就会存在一定的误判率。
  • 删除问题,我们如果把数组某个位置设置为 1 或者是 0 ,那么识别会影响其它数据的判断,

实现

而对于我们java 实现的bloom过滤器可以通过谷歌提供的guava ,但是我们现在的系统大部分都是分布式系统,那么这种方式就不太合适,我们可能会想把数据存放到一个分布式环境下的内存中,自然就会想到redis ,那么redis的那帮大神们自然肯定也会想到,所以就有很多开源组织和个人研发了基于redis 的布隆过滤器。 那么首先介绍使用Redis Module 集成进RedisBloom。

  1. 下载官方的git 仓库文件 github.com/RedisBloom/…
  2. 比如在Linux 服务器上面可以直接通过git 拉取源码 git github.com/RedisBloom/…
  3. 进入下载好的目录 执行make 命令进行编译成so 库

  1. 接下来就很简单了,直接在redis.conf 配置文件里面,然后重启服务器
    loadmodule /opt/redis/redis-cluster/redisbloom.so
  2. 重启服务,进入 client 查看集成进来的module

相关bf 命令可以去https://github.com/RedisBloom/RedisBloom 查看

那么对与官网提供的其它模块的集成模式基本和RedisBloom一样,有兴趣的可以去尝试下,这样面试的时候可以提一下自己研究过这块,相信面试官会喜欢这样的面试人员。

那么如果用java 调用RedisBloom 先给大家看一下官网的实现方式。

// 核心还是通过jedis客户端的方式,这种方式还是笔者看它源码改写后的,官方提供的不支持redis 密码验证
pom 文件
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.0</version>
</dependency>

<dependency>
    <groupId>com.redislabs</groupId>
    <artifactId>jrebloom</artifactId>
    <version>2.0.0-SNAPSHOT</version>
</dependency>
<repositories>
    <repository>
        <id>snapshots-repo</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>
</repositories>

代码实现

public class RedisBloom {
    public static void main(String[] args) {
        JedisPoolConfig conf = new JedisPoolConfig();
        conf.setMaxTotal(100);
        conf.setTestOnBorrow(false);
        conf.setTestOnReturn(false);
        conf.setTestOnCreate(false);
        conf.setTestWhileIdle(false);
        conf.setMinEvictableIdleTimeMillis(60000L);
        conf.setTimeBetweenEvictionRunsMillis(30000L);
        conf.setNumTestsPerEvictionRun(-1);
        conf.setFairness(true);
        JedisPool jedisPool = new JedisPool(conf,"192.168.13.131",6379,30000,"123456");
        Client client = new Client(jedisPool);

        boolean exists = client.exists("newFilter", "foo55");
        System.out.println(exists);
        client.add("newFilter","123");
        client.add("newFilter","456");
        client.add("newFilter","789");
        client.close();
    }
}

上面的这种实现方式比较复杂,下面给大家介绍一款redis 比较牛皮的客户端工具,可能大家听说过 Redission 框架,可能大家了解的是它实现分布式锁忒简单,这个框架提供了很多强大的功能, 比如分布式锁、令牌桶限流、分布式布隆过滤器,那么我们就来爽一波

  <!-- 引入redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.5</version>
        </dependency>
   
 
 编写 ReissonConfig
   /**
 * @Description
 * @Author gavin
 * @Since 1.0
 * @Date 2019/10/27
 */
@Configuration
public class RedissonConfig  {

    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.13.132:6379")
        return Redisson.create(config);
    }
}
   
// 分布式信号实现秒杀库存  利用ab 测试工具可以测试
    @GetMapping("/kill2")
    public void kill2(){
        RSemaphore semaphore = redissonClient.getSemaphore("number");
        // 默认抢到会减少一个
        boolean tryAcquire = semaphore.tryAcquire();
        if(tryAcquire){
            Integer number = redisUtil.get("number", Integer.class);
            if(number > 0){
                System.out.println("当前库存还剩:"+number+"   用户抢购成功");
            }
        }
    }
// 分布式环境下布隆过滤器的实现,非常的简单    
 @Test
 public void testBloomFilter() {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("newFilter2");
        // 初始化布隆过滤器,预计统计元素数量为55000000,期望误差率为0.03
        bloomFilter.tryInit(55000000L, 0.03);
        bloomFilter.add("lisi");
        bloomFilter.add("wangwu");
        boolean foo = bloomFilter.contains("lisi343");
        System.out.println(foo);
    }
    
//  限流

  public void testRateLimiter() {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("myRateLimiter");
        // 初始化
        // 最大流速 = 每1秒钟产生10个令牌
        boolean trySetRate = rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
        if(rateLimiter.tryAcquire()){
            System.out.println("我获取到了信息哈");
        }else{
            System.out.println("对不起你被限流了哈");
        }
    }

欧克欧克😀 大概这里都介绍完毕了,如果大家想研究更多Redisson 的强大功能,这里给一个传送地址 github.com/redisson/re… 绝壁不会让你失望