Java SpringBoot使用guava过滤器

目录
  • 介绍
  • 特点
  • 数据结构
    • 新增数据
    • 查询数据
    • 删除数据
  • 作用
  • 业务使用
  • 快速开始

介绍

布隆过滤器(BloomFilter)是一种数据结构,特点是高效地插入和查询,可以用来告诉你某个key一定不存在或者可能存在,它是用多个哈希函数,将一个key映射到多个数组下标中。

特点

布隆过滤器可以告诉我们 "某样东西一定不存在或者可能存在",也就是说布隆过滤器说这个key不存在则一定不存在,布隆过滤器说这个key存在则可能不存在(会存在误判),利用这个判断是否存在的特点可以做很多有趣的事情。

数据结构

由一串很长的二进制数组向量组成,可以将其看成一个二进制数组。既然是二进制,那么里面存放的不是0,就是1,但是初始默认值都是0。

新增数据

把一个key经过多个hash函数计算出多个数组下标,把计算出的下标的位置的0变成1。

查询数据

当要查询一个key是否在布隆过滤器中,用相同的方法计算出多个1,判断根据这个key计算出的多个1的位置在布隆过滤器中是否都是1,如果都是那么代表可能存在,如果不都是代表一定不存在。

删除数据

布隆过滤器很难进行删除操作。

作用

高效判断一个数据在不在某一组数据中。

误差率: 创建布隆过滤器时,可以设置误差率,误差率越小,hash函数越多,二进制数据占用空间越大。hash函数越多,计算出的hash值多,对应计算出的二进制数组下标越多,效率越低,有一个位置在布隆过滤器中置不为1,就代表不存在,精确度越高。

业务使用

布隆过滤器就是在Redis缓存前面在加一层过滤,负责过滤无效的请求,如果请求没有通过布隆过滤器就不会到缓存和数据库。

布隆过滤器的优点 :

  • 空间效率 和 查询效率都远远超过一般的算法。
  • 增加和查询元素的时间复杂为O(N)(N为哈希函数的个数,通常情况比较小)。
  • 占用空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间。
  • 保密性强,布隆过滤器不存储数据本身。

布隆过滤器的缺点: 有点一定的误判率,但是可以通过调整参数来降低。

  • 无法获取元素本身。
  • 很难删除元素。

快速开始

导入依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>

配置类:

把布隆过滤器对象注入进IOC容器。

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.nio.charset.Charset;
@Configuration
public class BloomFilterConfig {
    /**
     * expectedInsertions:期望添加的数据个数
     * fpp:期望的误判率,期望的误判率越低,布隆过滤器计算时间越长
     * @return
     */
    @Bean
    public BloomFilter<String> goodsIDBloom(){
        BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001);
        return filter;
    }
    @Bean
    public BloomFilter<String> orderBloom(){
        BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001);
        return filter;
    }
}

启动项目时把所有key插入布隆过滤器:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.hash.BloomFilter;
import com.liu.seckill.entity.SeckillGood;
import com.liu.seckill.service.SeckillGoodService;
import com.liu.seckill.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Configuration
public class InitConfig implements InitializingBean {
    @Autowired
    UserService userService;
    @Autowired
    SeckillGoodService seckillGoodService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    RedisConfig redisConfig;
    @Autowired
    @Qualifier("goodsIDBloom")
    BloomFilter<String> goodsIDBloom;
    /**
     * 把商品库存加载到 Redis中
     * 每天更新
     * @throws Exception
     */
    @Override
    @Scheduled(fixedRate = 1000*60*60*24)
    public void afterPropertiesSet() throws Exception {
        //只缓存秒杀还没结束或秒杀还没开始的商品
        List<SeckillGood> seckillGoods = seckillGoodService.list(new QueryWrapper<SeckillGood>().ge("end_time", LocalDateTime.now()));
        if (seckillGoods == null) {
            log.info("暂无秒杀商品");
            return;
        }
        //将秒杀商品ID和库存分别存入redis中
        List<Long> seckillGoodIDList = new ArrayList<>();
        for (SeckillGood seckillGood : seckillGoods) {
            seckillGoodIDList.add(seckillGood.getSgId());
            //设置过期时间
            long millis = Duration.between(LocalDateTime.now(), seckillGood.getEndTime()).toMillis();
            redisTemplate.opsForValue().set("seckillGoodID:" + seckillGood.getSgId(), seckillGood.getSgStock(), millis, TimeUnit.MILLISECONDS);
            //存储值到布隆过滤器中
            goodsIDBloom.put(seckillGood.getSgId()+"");
        }
        redisTemplate.opsForValue().set("seckillGoodIDList:", seckillGoodIDList,1,TimeUnit.DAYS);

    }
}

使用布隆过滤器:

查询redis之前先走布隆过滤器,如果布隆过滤器判断没有则一定没有。

@RestController
@RequestMapping("/seckillOrder")
public class SeckillOrderController  {
 	//如果有多个布隆过滤器,就同时使用@Qualifier和@Autowired
    @Autowired
    @Qualifier("goodsIDBloom")
    BloomFilter<String> goodsIDBloom;
    @Autowired
    @Qualifier("orderBloom")
    BloomFilter<String> orderBloom;

    @GetMapping("/seckillGoods/{goodId}")
    public Result seckillGoods(@PathVariable("goodId") Long goodId) {
        ValueOperations valueOperations = redisTemplate.opsForValue();

        //判断秒杀商品是否存在
        //如果商品id在布隆过滤器中存在,那么就要再去判断在不在redis中,在,才能证明真的在
        if (goodsIDBloom.mightContain(goodId+"")){
            ArrayList<Long> seckillGoodIDList = (ArrayList<Long>) valueOperations.get("seckillGoodIDList:");
            Assert.isTrue(seckillGoodIDList.contains(goodId),"未找到该商品,商品ID有误或此商品不参与秒杀或此商品秒杀已结束");
        }else {
            Assert.isTrue(false,"未找到该商品,商品ID有误或此商品不参与秒杀或此商品秒杀已结束");
        }
    }
}

到此这篇关于Java SpringBoot使用guava的布隆过滤器的文章就介绍到这了,更多相关SpringBoot使用guava 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot之跨域过滤器配置允许跨域访问方式

    目录 SpringBoot跨域过滤器配置允许跨域访问 跨域请求 问题背景 跨域过滤器 跨域功能改进 SpringBoot跨域设置(CORS) 一.什么是跨域 二.跨域资源共享(CORS) 1. 简单请求 2. 非简单请求 三.SpringBoot设置CORS SpringBoot跨域过滤器配置允许跨域访问 跨域请求 当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求. 出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求. 例如,XMLHttp

  • springboot使用GuavaCache做简单缓存处理的方法

    问题背景 实际项目碰到一个上游服务商接口有10秒的查询限制(同个账号). 项目中有一个需求是要实时统计一些数据,一个应用下可能有多个相同的账号.由于服务商接口的限制,当批量查询时,可能出现同一个账号第一次查询有数据,但第二次查询无数据的情况. 解决方案 基于以上问题,提出用缓存的过期时间来解决. 这时,可用Redis和Guava Cache来解决: 当批量查询时,同一个账号第一次查询有数据则缓存并设置过期时间10s, 后续查询时直接从缓存中取,没有再从服务商查询. 最终采用Guava Cache

  • SpringBoot中shiro过滤器的重写与配置详解

    目录 问题 解决方案 实现代码 1.重写shiro 登录 过滤器 2.重写role权限 过滤器 3.配置过滤器 问题 遇到问题:在前后端分离跨域访问的项目中shiro进行权限拦截失效 (即使有正确权限的访问也会被拦截) 时造成302重定向错误等问题报错:Response for preflight is invalid (redirect) 1.302原因:使用ajax访问后端项目时无法识别重定向操作 2.shiro拦截失效原因:跨域访问时有一种带预检访问的跨域,即访问时先发出一条methods

  • 解析springboot整合谷歌开源缓存框架Guava Cache原理

    目录 Guava Cache:⾕歌开源缓存框架 Guava Cache使用 使用压测⼯具Jmeter5.x进行接口压力测试: 压测⼯具本地快速安装Jmeter5.x 新增聚合报告:线程组->添加->监听器->聚合报告(Aggregate Report) Guava Cache:⾕歌开源缓存框架 Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效.Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache: 愿意消耗一些内存空间

  • Springboot整合GuavaCache缓存过程解析

    这篇文章主要介绍了springboot整合GuavaCache缓存过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Guava Cache是一种本地缓存机制,之所以叫本地缓存,是因为它不会把缓存数据放到外部文件或者其他服务器上,而是存放到了应用内存中. Guava Cache的优点是:简单.强大.轻量级. GuavaCache适用场景: 1.某些接口或者键值会被查询多次以上: 2.愿意使用或牺牲一些内存空间来提升访问或者计算速度: 3.缓

  • Springboot详解如何实现SQL注入过滤器过程

    目录 1.过滤器SqlInjectFilter 2.请求装饰类CustomRequestWrapper 3.过滤器注册 4.测试辅助类 4.1 结果对象ResultObj 4.2 Restful的Controller类 5.测试 5.1 POST请求测试 5.2 GET请求测试1 5.3 GET请求测试2 场景:以过滤器(Filter)的方式,对所有http请求的入参拦截,使用正则表达式匹配入参中的字符串.存在SQL注入风险的参数,中断请求,并立即返回提示信息.不存在SQL注入风险的参数,校验通

  • SpringBoot加入Guava Cache实现本地缓存代码实例

    这篇文章主要介绍了SpringBoot加入Guava Cache实现本地缓存代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在pom.xml中加入guava依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version>

  • SpringBoot+Redis布隆过滤器防恶意流量击穿缓存

    目录 什么是恶意流量穿透 怎么防 布隆过滤器的另一个用武场景 给Redis安装BloomFilter 在Redis里使用BloomFilter 结合SpringBoot使用 搭建springboot工程 使用压测工具喂120万条数据进入RedisBloomfilter看实际效果 本文主要介绍了SpringBoot+Redis布隆过滤器防恶意流量击穿缓存,具体如下: 什么是恶意流量穿透 假设我们的Redis里存有一组用户的注册email,以email作为Key存在,同时它对应着DB里的User表的

  • springboot @WebFilter注解过滤器的实现

    @WebFilter注解过滤器 @WebFilter加在过滤器的注解上使用 import lombok.extern.slf4j.Slf4j; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; impo

  • 解决springboot中配置过滤器以及可能出现的问题

    在springboot添加过滤器有两种方式: 1.通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中需要增加cookie可能会存在写不进前端情况) 2.通过注解@WebFilter的方式 通过创建FilterRegistrationBean的方式创建多个filter以及设置执行顺序: 1.创建两个实现Filter接口的类TestFilter1 .TestFilter2 package com.aoxun.c

  • Java中使用Filter过滤器的方法

    Filter过滤器 着重记录下 public void doFilter(){} 方法参数: (1) ServletRequest servletRequest 请求reqeust: (2)ServletResponse servletResponse 响应response: (3)FilterChain filterChain 参数 filterChain,有一个doFilter() 方法,调用这个方法,可以使程序继续往下走,执行后续代码,如果有多个过滤器,它会继续执行下一个过滤器逻辑(开发中

  • java~springboot~ibatis数组in查询的实现方法

    在ibatis的xml文件里,我们去写sql语句,对应mapper类的方法,这些sql语句与控制台上没什么两样,但在有些功能上需要注意,如where in这种从数组里查询符合条件的集合里,需要在xml里进行特别的处理. <update id="batchUpdate" parameterType="map"> update customer_info set status=#{status},appoint_time=#{appointTime} whe

  • java springboot poi 从controller 接收不同类型excel 文件处理

    根据poi接收controller层的excel文件导入 可使用后缀名xls或xlsx格式的excel. 1.pom引入 <!-- poi 操作Excel --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <d

  • 浅谈Java(SpringBoot)基于zookeeper的分布式锁实现

    通过zookeeper实现分布式锁 1.创建zookeeper的client 首先通过CuratorFrameworkFactory创建一个连接zookeeper的连接CuratorFramework client public class CuratorFactoryBean implements FactoryBean<CuratorFramework>, InitializingBean, DisposableBean { private static final Logger LOGG

  • Java实现简单文件过滤器功能

    代码思路: 想要循环遍历文件夹下所有子文件夹,就要用到递归. 首先判断路径是否存在: 是:获取文件 判断是否文件夹: 是:调用自身,继续获取子文件夹下内容 否:判断文件后缀,符合则输出 否:输出提示 package com.hanqi.maya.util; import java.io.File; import java.util.Scanner; public class TestGuolv { public static void main(String[] args) { Scanner

随机推荐