Redis布隆过滤器

Redis布隆过滤器

01、什么是布隆过滤器

缓存穿透故障、幂等性判断、今日头条,抖音的推荐

相信很多小伙伴在预览的时候,头条会根据的你喜好,为你推荐一些图文信息供你阅读,比如:你喜欢汽车、喜欢美女,它就会经常推荐一些美女图或者汽车文章供你阅读。

我们今天讲的内容不是推荐算法,而是:已读去重算法。

推荐引擎给你推荐合适的文章,要过滤你已经阅读过的,不然就会重复给你推荐。比如你阅读了一篇文章,如果你已经阅读了,如果没有已读去重,明天又给你推荐。故为了达到更好的体验效果必须把你读过的内容去重

这种已读去重的技术解决方案,一般会想到数据库存储。这种方案对于一线互联网公司来说是有问题的。像今日头条这种高并发的场景,每次推荐都去数据库查询,必定==导致数据库扛不住==。所以肯定是redis缓存来处理的。

既然用到了redis,想一想用什么数据结构?

大部分人会想到set。

set是一种很不错的去重数据结构,对于小量数据可以实现,但是对于大数据,例如:几个月的已读历史数据的存储,set就不太适合了。业界的做法一般是使用==布隆过滤器==来实现。

02、安装

redis本身是没有布隆过滤器的。它是redis开发的一个插件,需要进行集成和配置才可以使用,具体如下:

下载地址(redis官网下载即可):https://github.com/RedisLabsModules/redisbloom/

开发文档:https://github.com/RedisBloom/JRedisBloom

2.1 下载bloomFilter
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
tar -zxvf v1.1.1.tar.gz
2.2 插件编译
cd RedisBloom-1.1.1
make

成功后可看到目录下有个.so文件

2.3 Redis整合boomfilter

1、在redis.conf配置文件里加入如下引入配置

# redis集群每个配置文件都需要加入这一行
loadmodule /example/redis/RedisBloom-2.2.1

2、添加完配置后重启redis

>cd redis的安装目录
>ps -ef | grep redis 
>kill redispid
>src/redis-server ./redis.conf

3、通过客户端连接redis服务

[root@iZwz94p9y07ns86pck1l2jZ redis-6.2.4]# src/redis-cli 
127.0.0.1:6379> auth mkxiaoer
OK
127.0.0.1:6379> bf.add filter 2
(integer) 1

03、BloomFilter的命令

BF.RESERVE {key} {error_rate} {capacity}
  • error_rate:错误率,允许布隆过滤器的错误率。值越低,过滤器的位数组越大,占用空间也就越大。
  • capacity:存储的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。

注意:必须在add之前使用bf.reserve命令显示的创建。

如果对应的key,已经存在,bf.reserve会报错。

如果不使用bf.reserve,默认的:error_rate是0.0001;capacity是10000

127.0.0.1:6379> bf.reserve filter 0.01 100
OK
127.0.0.1:6379> bf.add filter 1
(integer) 1
127.0.0.1:6379> bf.exists filter 1
(integer) 1
127.0.0.1:6379> bf.madd filter 1
1) (integer) 0
127.0.0.1:6379> bf.madd filter 1 2 3 4 5 6
1) (integer) 0
2) (integer) 1
3) (integer) 1
4) (integer) 1
5) (integer) 1
6) (integer) 1
127.0.0.1:6379> bf.madd filter 1 2 3 4 5 6 
1) (integer) 0
2) (integer) 0
3) (integer) 0
4) (integer) 0
5) (integer) 0
6) (integer) 0
127.0.0.1:6379> bf.madd filter 1 2 3 4 5 6 7 6
1) (integer) 0
2) (integer) 0
3) (integer) 0
4) (integer) 0
5) (integer) 0
6) (integer) 0
7) (integer) 1
8) (integer) 0
127.0.0.1:6379> bf.mexists filter 1 2 3 4 5 6 7 6
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 1
5) (integer) 1
6) (integer) 1
7) (integer) 1
8) (integer) 1

04、使用布隆过滤器解决缓存穿透

4.1 增加布隆过滤器的依赖包
<dependency>
    <groupId>com.redislabs</groupId>
    <artifactId>jrebloom</artifactId>
    <version>2.1.0</version>
</dependency>
4.2 配置封装
redis:
  bloom:
    host: 
    port: 6379
    password: 
    capacity: 100
    rate: 0.01
4.3 布隆过滤的配置类
package com.example.config;

import io.rebloom.client.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisBloomConfiguration {

    private static final Logger log = LoggerFactory.getLogger(RedisBloomConfiguration.class);

    // host地址
    @Value("${redis.bloom.host}")
    private String host;
    // host地址
    @Value("${redis.bloom.password}")
    private String password;
    // 端口
    @Value("${redis.bloom.port}")
    private Integer port;
    // 基数
    @Value("${redis.bloom.capacity}")
    private Integer capacity;
    // 错误率
    @Value("${redis.bloom.rate}")
    private Double rate;

    @Bean
    public JedisPool jedisPool() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(8);
        poolConfig.setMaxTotal(8);
        poolConfig.setMaxWaitMillis(5 * 1000);
        JedisPool jp = new JedisPool(poolConfig, host, port,
                3 * 1000, password, 0);
        return jp;
    }

    @Bean
    public Client rebloomClient(JedisPool pool) {
        // 1: 初始化布隆过滤器的client对象
        Client client = new Client(pool);
        try {
            // 初始化bloomfilter的容器错率
            client.createFilter("redis:bloom:filter", capacity, rate);
        } catch (Exception ex) {
            log.info("bloom过滤器已经存在,异常信息是:{}", ex.getMessage());
        }
        return client;
    }
}
4.4 初始化数据到布隆中
package com.example.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.Category;
import com.example.mapper.CategoryMapper;
import io.rebloom.client.Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.List;

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    @Autowired
    private Client rebloomClient;

    /**
     * 把需要进行过滤的数据进行放入到布隆过滤器中
     */
    @PostConstruct
    public void initBloomData() {
        List<Category> categroies = this.findCategory(0);
        if (!CollectionUtils.isEmpty(categroies)) {
            // 把对应一级分类的id放入到bloomfilter中
            String[] ids = new String[categroies.size()];
            for (int i = 0; i < categroies.size(); i++) {
                ids[i] = categroies.get(i).getId() + "";
            }
            // bf.madd
            rebloomClient.addMulti("redis:bloom:category", ids);
        }
    }

    @Override
    public List<Category> findCategory(Integer cid) {
        QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("pid", cid);
        return this.list(queryWrapper);
    }
}

问题1:如果后面的元素要追加到布隆过滤器中,如何追加?

  • 定时任务
  • 重启服务(一定灰度发布),比如三台服务器,停止一台,启动一台,然后再停一台再启动一台。以此类推
  • 把数据添加到布隆过滤器中
  • MQ同步
4.5 布隆过滤器解决缓存穿透的问题
@Autowired
    private CategoryService categoryService;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private Client reBloomClient;


    @GetMapping("/findCategory/{cid}")
    public List<Category> findCategory(@PathVariable("cid") Integer cid) {
        // 判断分类id是否传入,如果没有传入那么直接返回
        KAssert.isEmpty(cid, 401, "分类不存在");
         // 进行布隆过滤 bf.madd redis:bloom:category “1” "2" "3" "4" "5"
         // 进行布隆过滤 bf.mexists redis:bloom:category “1”
        boolean[] booleans = reBloomClient.existsMulti("redis:bloom:category", cid + "");
         // 如果cid没有在布隆过滤器中,说明你数据不存在,直接返回
        if (!booleans[0]) {
            throw new ValidationException(401, "分类不存在");
        }

        // BloomFilter的认识
        // 用了Redis缓存就真的不会进入数据库了吗?
        List<Category> categoryList = new ArrayList<>();
        // 先去缓存中去获取分类信息
        String categories = (String) redisTemplate.opsForValue().get("subCid:" + cid);
        if (StringUtils.isEmpty(categories)) {
            log.info("db去查询了........");
            categoryList = categoryService.findCategroies(cid);
            if (!CollectionUtils.isEmpty(categoryList)) {
                redisTemplate.opsForValue().set("subCid:" + cid, JsonUtil.obj2String(categoryList));
            }
        } else {
            categoryList = JsonUtil.string2Obj(categories, List.class, Category.class);
        }
        return categoryList;
    }

05、布隆过滤器的问题和使用场景

  • 因为布隆过滤器存在一定的误判率,如果业务场景是严格要求的话,不适合使用布隆过滤器。

  • 黑名单

  • 抖音、今日头条数据已读去重过滤

  • 要对平台用户的行为进行监控,然后根据用户行为,把用户喜欢的内容推送给用户。

  • 大数据分析平台

  • 拦截垃圾邮件和IP

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南宫拾壹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值