胖胖的枫叶
主页
博客
产品设计
企业架构
全栈开发
效率工具
数据分析
项目管理
方法论
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
主页
博客
产品设计
企业架构
全栈开发
效率工具
数据分析
项目管理
方法论
面试
  • openJdk-docs
  • spring-projects-docs
  • mysql-docs
  • redis-commands
  • redis-projects
  • apache-rocketmq
  • docker-docs
  • mybatis-docs
  • netty-docs
  • journaldev
  • geeksforgeeks
  • 后端进阶
  • 并发编程网
  • 英语肌肉记忆锻炼软件
  • 墨菲安全
  • Redisson-docs
  • jmh-Visual
  • 美团技术
  • MavenSearch
  • 标签索引
  • 2024年

    • 配置Mac环境
    • 业务知识会计管理
    • 业务知识会计基础
    • 业务知识什么是财务
  • 2023年

    • 项目 Boi
  • 2022年

    • 企业架构故障管理
    • 企业架构开发债务
  • 2021年

    • Python3.8 Matplotlib员工数据分析
    • Python3.8 Matplotlib IP折线图
    • Python3.8 词云 IP地址
    • Redis RediSearch
    • Rust第一个CLI程序
    • Rust所有权
    • Rust函数与控制流
    • Rust变量与数据类型
    • Rust入门
    • 企业架构分布式系统
    • 编程式权限设计
    • Java JVM优化
    • SpringBoot MyBatis 批量
    • SpringBoot 测试Mock
    • SpringBoot Redis布隆过滤器
    • CentOS7 Jenkins 部署
    • SpringBoot WebClient
    • Docker Drone 部署
    • SpringBoot MyBatis
    • SpringBoot Redisson
    • SpringBoot MyBatis 雪花算法
    • Java Netty
    • Redis 扫描
    • CentOS7 Jenkins本地部署分级
    • Mac 安装 Neo4j Jupyter
    • Mac OpenJDK11 JavaFX 环境
    • Mac 安装 Jenv
    • SpringBoot Redis 延时队列
    • SpringBoot MDC日志
    • SpringBoot 定时任务
    • CentOS7 Nginx GoAccess
    • SpringBoot MyBatis 分析
    • SpringBoot Lucene
    • 企业架构分布式锁
    • 学习技巧减少学习排斥心理
    • SpringBoot 动态数据源
    • Docker Compose SpringBoot MySQL Redis
    • SpringBoot 阻塞队列
    • Docker Compose Redis 哨兵
    • Docker Compose Redis 主从
    • 网络通信
  • 2020年

    • SpringBoot 延时队列
    • MySQL基础(四)
    • Java 雪花算法
    • Redis Geo
    • 网络通信 Tcpdump
    • Spring SPI
    • Java Zookeeper
    • SpringBoot JMH
    • 网络通信 Wireshark
    • Docker Compose Redis MySQL
    • CentOS7 Docker 部署
    • Netty 源码环境搭建
    • MySQL基础(三)
    • CentOS7 Selenium运行环境
    • CentOS7 Nginx HTTPS
    • Java JMH
    • SpringBoot 修改Tomcat版本
    • Java Eureka 钉钉通知
    • SpringBoot 错误钉钉通知
    • Java JVM
    • Git 合并提交
    • CentOS7 OpenResty 部署
  • 2019年

    • Redis CLI
    • CentOS7 Nginx 日志
    • 编程式代码风格
    • IDEA 插件
    • Skywalking 源码环境搭建
    • SpringBoot Redis 超时错误
    • 编程式 gRPC
    • Java Arthas
    • Docker Compose Redis 缓存击穿
    • Docker ElasticSearch5.6.8 部署
    • Docker Mysql5.7 部署
    • Spring Redis 字符串
    • Docker Zookeeper 部署
    • Docker Redis 部署
    • SpringBoot Dubbo
    • CentOS7 CMake 部署
    • 应用程序性能指标
    • Java Code 递归
    • CentOS7 ELK 部署
    • CentOS7 Sonarqube 部署
    • Java Selenium
    • Java JJWT JUnit4
    • Spring 源码环境搭建
    • Java JUnit4
    • Java Web JSON Token
    • 编程式 FastDFS
    • Java XPath
    • Redis基础(二)
    • Redis基础(一)
    • Java MyBatis JUnit4
    • Java MyBatis H2 JUnit4
    • MyBatis 源码环境搭建
    • Git 配置
    • Java 核心
    • Java Dubbo
    • Java JavaCollecionsFramework
    • Java Maven
    • Java MyBatis
    • Java Spring
    • Java SpringMVC
    • MySQL
    • Redis
  • 2018年

    • Java HashMap
    • Java HashSet
    • Java Code 交换值
    • Spring Upgrade SpringBoot
    • Mac 编程环境
    • Java Log4j
    • 网络通信 Modbus
    • MySQL基础(二)
    • MySQL基础(一)
    • Java Stack
    • Java Vector
    • CentOS7 RabbitMQ 部署
    • CentOS7 Redis 部署
    • CentOS7 MongoDB 部署
    • CentOS7 基础命令
    • Java Eureka Zookeeper
    • CentOS7 MySQL 部署
    • Git 分支
    • CentOS7 Java环境配置
    • Java LinkedList
    • Java ArrayList
    • Spring Annotation Aop

SpringBoot Redis布隆过滤器

Redis 中不能直接使用布隆过滤器,Redis 4.0 版本之后提供的 modules(扩展模块)

布隆过滤器的原理

  • 数据结构使用一位的数组,每次存储键值的时候,不是直接把数据存储在数据结构中。而是经过hash运算。将此元素的 hash 值均匀的存储在位数组中。把这些位置设置成 1 就完成了添加操作。
  • 判断元素是否存在时,经过运算判断对应的位置是否为全部1即可。因此布隆过滤器查询此值存在时,此值不一定存在,但查询此值不存在时,此值一定不存在。
  • 但是存在一定的误差,并且当位数组存储值比较稀疏的时候,查询的准确率越高,而当位数组存储的值越来越多时,误差也会增大。

  • 空间占用
    • 布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是误判率p。公式根据这两个输入得到两个输出,第一个输出是位数组的长度m,也就是需要的存储空间大小(bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到误判率,最佳的数量会有最低的误判率。
  • 计算公式
k≈0.7*(m/n)    
p≈0.6185^(m/n) 
  • 位数组m越长,误判率p就越低;
  • 位数组m越长,hash函数最佳数量k就越多,影响计算效率;

演示代码

安装Redis

演示情况直接使用docker来安装,免去下载编译过程。

docker run -p 6379:6379 -d redislabs/rebloom:latest
  • 使用redis-cli或者客户端工具进入
➜  docker-run redis-cli 
127.0.0.1:6379> bf.add 1 1
(integer) 0
127.0.0.1:6379> bf.add 1 2
(integer) 1
127.0.0.1:6379> 
  • 如果没有提示错误则表示安装成功。

Bloom基础用法

布隆过滤器的命令不是很多,主要包含以下几个

  • bf.add:添加元素
  • bf.exists:判断某个元素是否存在
  • bf.madd:添加多个元素
  • bf.mexists:判断多个元素是否存在
  • bf.reserve:设置布隆过滤器的准确率 只能在未设置数据的时候设置,否则会报错
127.0.0.1:6379> bf.exists 1 1
(integer) 1
127.0.0.1:6379> bf.exists 1 2
(integer) 1
127.0.0.1:6379> bf.reserve 1 0.01 100 # 已经存在集合在设置会报错
(error) ERR item exists
127.0.0.1:6379> bf.reserve user 0.01 100 # 未设置数据的则可以正常设置。
OK

SprngBoot演示

/**
 * @author z201.coding@gmail.com
 **/
@Component
@Slf4j
public class BloomTool {


    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 初始化
     *
     * @param key
     * @param rate 准确度
     * @param size 大小
     */
    public Boolean init(String key, String rate, String size) {
        try {
            String script = "return redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
            DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<String>();
            defaultRedisScript.setResultType(String.class);
            defaultRedisScript.setScriptSource(new StaticScriptSource(script));
            String[] argv = new String[]{rate,size};
            String result = (String) redisTemplate.execute(defaultRedisScript, Collections.singletonList(key), argv);
            if (Objects.equals(result, "OK")) {
                return true;
            }
        }catch (RedisSystemException redisSystemException){
            log.error("{}",redisSystemException.getMessage());
        }
        return false;
    }

    /**
     * 添加元素
     *
     * @param key
     * @param value
     */
    public Boolean add(String key, String value) {
        String script = "return redis.call('bf.add', KEYS[1], ARGV[1])";
        return lua(script, key, value);
    }

    /**
     * 是否存在
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean exists(String key, Object value) {
        String script = "return redis.call('bf.exists', KEYS[1], ARGV[1])";
        return lua(script, key, value);
    }

    private Boolean lua(String script, String key, Object value) {
        DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<Long>();
        defaultRedisScript.setResultType(Long.class);
        defaultRedisScript.setScriptSource(new StaticScriptSource(script));
        Long result = (Long) redisTemplate.execute(defaultRedisScript, Collections.singletonList(key), value);
        if (Objects.equals(result, 1L)) {
            return true;
        }
        return false;
    }

}

  • 单元测试
package cn.z201.redis;

@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = AppApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
// 指定单元测试方法顺序
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AppApplicationTest {

    @Autowired
    BloomTool bloomTool;

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    @Disabled
    public void testBloom() {
        Set<String> keys =  redisTemplate.keys("*");
        log.info("{}",keys);
        redisTemplate.delete(keys);
        String key = "u";
        log.info("init 异常或者错误都会报错 {}", bloomTool.init(key, "0.001", "10000000"));
        List<String> values = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Integer value = RandomUtil.randomInt(1000, 20000000);
            values.add(value.toString());
            Boolean result =  bloomTool.add(key, value.toString());
            log.info("bf.add {} {} {} ", result, key, value);
        }
        Integer index = RandomUtil.randomInt(0, values.size());
        String value = values.get(index);
        Boolean result = bloomTool.exists(key, value);
        log.info("bf.exists {} {} {} ", result, key, value);
    }

}
  • Console
[main]  [u]
[main]  init 异常或者错误都会报错 true
[main]  bf.add true u 11569209 
[main]  bf.add true u 12786658 
[main]  bf.add true u 16272560 
[main]  bf.add true u 1212058 
[main]  bf.add true u 4896410 
[main]  bf.add true u 15218411 
[main]  bf.add true u 13349975 
[main]  bf.add true u 12544373 
[main]  bf.add true u 4507958 
[main]  bf.add true u 12143750 
[main]  bf.exists true u 1212058 

END

最近更新: 2025/12/27 18:51
Contributors: 庆峰
Prev
SpringBoot 测试Mock
Next
CentOS7 Jenkins 部署