springboot+redis布隆过滤器数据

分布式BloomFilter实现
 

布隆算法是一个以牺牲一定的准确率来换取低内存消耗的过滤算法,可以实现大量数据的过滤、去重等操作。

利用Redis的BitMap实现布隆过滤器的底层映射。


BloomFilter

请求到达正式业务之前, 判断该请求是否有效
维护一个大的bit数组, 把有效key的一次或多次的hash索引位置标志已存在. 当有请求进来时, 计算进来的key的hash索引, 判断每一个索引的值是否为true.
常用于处理缓存穿透问题


选用redis实现的好处


部署高可用节点时, 减少每一个节点的开销
redis可以持久化, 避免因服务器宕机导致需要重新灌数据的开销
redis中可实现的数组长度更长,大数据量下,hash索引可散列的范围更大


实现
索引个数,bit数组长度

使用google中根据原有数据长度计算索引个数, bit数组长度的方法


hash算法


使用一个key衍生出多个key, 根据对bit长度求余的hash算法


initBloomFilter方法


根据数据的长度, 计算hash个数,和bit数组长度, 将以上信息和容错率信息保存到redis中(这样可以实现多个过滤器)
使用通道将信息,所有数据压到redis中,使用通道可以提高效率,减少网络开销(测试时初始数据,可以减少75%时间)
 

首先看下项目需要的jar包
 

         <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
       <!--借助guava的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

 

代码

package com.until;

import com.google.common.base.Preconditions;
import com.google.common.hash.Funnel;
import com.google.common.hash.Hashing;

public class BloomFilterHelper<T> {

    private int numHashFunctions; //hash循环次数

    private int bitSize; //bitsize长度

    private Funnel<T> funnel;

    /**
     * @param funnel
     * @param expectedInsertions 期望插入长度
     * @param fpp 误差率
     */
    public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {
        Preconditions.checkArgument(funnel != null, "funnel不能为空");
        this.funnel = funnel;
        bitSize = optimalNumOfBits(expectedInsertions, fpp);
        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
    }

    /**
     * 计算bit数组长度
     */
    int[] murmurHashOffset(T value) {
        int[] offset = new int[numHashFunctions];

        long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
        int hash1 = (int) hash64;
        int hash2 = (int) (hash64 >>> 32);
        for (int i = 1; i <= numHashFunctions; i++) {
            int nextHash = hash1 + i * hash2;
            if (nextHash < 0) {
                nextHash = ~nextHash;
            }
            offset[i - 1] = nextHash % bitSize;
        }

        return offset;
    }

    /**
     * 计算bit数组的长度
     */
    private int optimalNumOfBits(long n, double p) {
        if (p == 0) {
            p = Double.MIN_VALUE;
        }
        return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }

    /**
     * 计算hash方法执行次数
     */
    private int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }



}

 

 

package com.until;

import com.google.common.base.Preconditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * @Author : JCccc
 * @CreateTime : 2020/4/23
 * @Description :
 **/
@Service
public class RedisBloomFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据给定的布隆过滤器添加值
     */
    public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            System.out.println("key : " + key + " " + "value : " + i);
            redisTemplate.opsForValue().setBit(key, i, true);
        }
    }

    /**
     * 根据给定的布隆过滤器判断值是否存在
     */
    public <T> boolean includeByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            System.out.println("key : " + key + " " + "value : " + i);
            if (!redisTemplate.opsForValue().getBit(key, i)) {
                return false;
            }
        }

        return true;
    }


}

 

调用:

package com.controller;



import com.google.common.base.Charsets;
import com.google.common.hash.Funnel;
import com.until.BloomFilterHelper;
import com.until.RedisBloomFilter;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.UUID;

@RequestMapping("/redisBloomFilter")
@Api(tags = "布朗过滤器", value = "redis相关接口")
@RestController
@Slf4j
public class RedisBloomFilterController {

    @Resource
    private RedisBloomFilter redisBloomFilter;


    BloomFilterHelper<String> myBloomFilterHelper = new BloomFilterHelper<>(
            (Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 150000, 0.00001);

    @GetMapping("/redis/bloomFilter")
    @ApiOperation("redis布隆过滤器数据添加")
    public void redisBloomFilter(){

        //List<String> allResourceId = redisBloomFilter.getAllResourceId();
        for (int i=0;i<10;i++) {
            String uuid = UUID.randomUUID().toString().replaceAll("-","");
            System.out.println("uuid:"+uuid);

            //将所有的资源id放入到布隆过滤器中
            redisBloomFilter.addByBloomFilter(myBloomFilterHelper,"bloom",uuid);
        }
        //return new ResponseResult(ResponseEnum.SUCCESS);
    }

    @GetMapping("/redis/bloomFilter/resourceId")
    @ApiOperation("redis布隆过滤器资源测试")
    public void redisBloomFilterResourceId(@RequestParam("resourceId")String resourceId){
        boolean mightContain = redisBloomFilter.includeByBloomFilter(myBloomFilterHelper,"bloom",resourceId);
        if (!mightContain){
            System.out.println("错误:"+mightContain);
            // return new QueryResult<>(ResCenterEnum.RESOURCE_EXSIT,"");
        }
        // return new ResponseResult(ResponseEnum.SUCCESS);
    }

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值