防止缓存击穿/缓存雪崩/缓存穿透-布隆过滤器(BloomFilter)在redis和redission中的应用

Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。
Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。
因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。

1.布隆过滤器设计原理

(1)包含m位的超大位数组(bitmap),每个值都是0;包含k个相互独立的哈希函数
(2)添加:将value进行y次(越大,准确率越高)哈希函数映射得到y个整数数值,以y个整数数值为下标修改bitmap位数组中值为1(如果本来就是1就不修改)
(3)判断:将searchValue进行y次哈希函数映射得到y个整数数值,然后在bigmap中找对应下标的值,
如果有下标数值不是1,结论:searchValue不在bitmap中
如果有下标数值都是1,结论:searchValue可能在bitmap中
2.redis+guava实现布隆过滤器
(1)redis环境安装

wget http://download.redis.io/releases/redis-5.0.8.tar.gz
tar -zxvf redis-5.0.8.tar.gz
mv redis-5.0.8 /usr/local
cd /usr/local
mv redis-5.0.8 redis
cd redis
make
make install
# 后台启动
nohup redis-server redis.conf &
# 连接
redis-cli -h 192.168.225.135 -p 6379
# 设置访问密码为123
config set requirepass 123
# 设置完成,需要输入密码才能操作数据
# 输入密码
auth 123
备注:redis重启数据丢失

vi /etc/sysctl.conf
# 增加一行
vm.overcommit_memory = 1
# 保存
:x
# 使配置文件生效
sysctl -p

(2)Java实现


    org.springframework.boot
    spring-boot-starter-data-redis


    com.google.guava
    guava
    25.1-jre

@Bean
public BloomFilterHelper initBloomFilterHelper() {
    return new BloomFilterHelper<>(
    (Funnel) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 
    1000000, // 集合大小
    0.01); // 错误率
}
/**
 * 初始化BloomFilter
 * @param funnel
 * @param expectedInsertions
 * @param fpp
 */
public BloomFilterHelper(Funnel funnel, int expectedInsertions, double fpp) {
    Preconditions.checkArgument(funnel != null, "funnel不能为空");
    this.funnel = funnel;
    // 计算bit数组长度
    bitSize = optimalNumOfBits(expectedInsertions, fpp);
    // 计算hash方法执行次数
    numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
}

/**
 * 执行hash算法
 * @param value
 * @return
 */
public 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;
    }
    int sizeOfBitArray = (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    return sizeOfBitArray;
}

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

3.使用redission实现布隆过滤器


  org.redisson
  redisson
  3.13.1

RBloomFilter rBloomFilter = redissonClient.getBloomFilter(key);
boolean isSuccess = rBloomFilter.tryInit(expectedInsertions, fpp);
if (!isSuccess){
    log.info("生成布隆过滤器失败");
    throw new RuntimeException("生成布隆过滤器失败");
}
bloomFilter.add(str);
bloomFilter.contains(str);

测试方法

public static void main(String[] args) {
  ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

  RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
  redisTemplate.delete(key);

  System.out.println("------test--start--------");
//    RedisService redisService = context.getBean(RedisService.class);

  BloomFilterService bloomFilterService = context.getBean(BloomFilterService.class);

  System.out.println("------add--start--------");

  Set set = new HashSet(1000);
  List list = new ArrayList(1000);
  for (int i = 0; i < 10000; i++) {
    String uuid = UUID.randomUUID().toString();
    if(i<1000){
      set.add(uuid);
      list.add(uuid);
    }
//      redisService.addByBloomFilter(key, uuid);
    bloomFilterService.add(key, uuid);
  }

  for (int i = 0; i < 10; i++) {
    String uuid = UUID.randomUUID().toString();
    list.add(uuid);
  }

  System.out.println("------search--start--------");

  for (String k: list){
    System.out.println("-------------" + k + "--------------");
//      System.out.println(redisService.includeByBloomFilter(key, k));
    System.out.println(bloomFilterService.contains(key, k));
    System.out.println("-------------" + k + "--------------");
  }
}

4.使用RedisBloom插件实现布隆过滤器

4.1 插件与redis配置关联

# 下载
wget https://github.com/RedisBloom/RedisBloom/archive/v2.2.4.tar.gz
# 解压
tar -zxvf v2.2.4.tar.gz 
# 进入解压目录之后make
make
# 成功后会生成一个  redisbloom.so  文件

# 切换到redis的目录中
vim redis.conf
# 修改 loadmodule 的值,配置 redisbloom.so 的存放位置
loadmodule /usr/local/redisBloom/redisbloom.so

4.2 在github中的介绍

https://github.com/RedisBloom/RedisBloom
https://github.com/RedisBloom/JRedisBloom

4.3 引入架包


  com.redislabs
  jrebloom
  2.0.0-m2

4.4 配置maven中的setting


  
    snapshots-repo
    https://oss.sonatype.org/content/repositories/snapshots
  

4.5 Java实现

# 单台redis
Client client = new Client(new JedisPool(new JedisPoolConfig(), "192.168.225.135",6379, 500, "123"));
#  可以定制,也可以不写
client.createFilter("specialBloom", 10000, 0.0001);
  # 集群分布式时
Set jedisClusterNodes = new HashSet<>();
// 地址集合
jedisClusterNodes.add(new HostAndPort("192.168.225.135", 6379));
ClusterClient client = new ClusterClient(jedisClusterNodes, 500, 500, 10, "123", new JedisPoolConfig());

完整项目地址在微信公众中,谢谢大家支持

Java技术学习笔记

你可能感兴趣的:(防止缓存击穿/缓存雪崩/缓存穿透-布隆过滤器(BloomFilter)在redis和redission中的应用)