赞
踩
Bloom 过滤器是一种概率型数据结构,用于快速判断一个元素是否属于一个集合。它以较小的空间占用和高效的查询时间著称。其具有判断为无则不存在,判断为有则不一定存在的特性。
(1)位数组(Bit Array):Bloom 过滤器使用一个固定长度的位数组来表示集合,并初始化为全0。每个元素通过多个哈希函数映射到位数组上的多个位置。
(2)哈希函数(Hash Function):Bloom 过滤器使用多个独立的哈希函数,每个哈希函数可以将一个元素映射到位数组的不同位置。常用的哈希函数包括 MurmurHash、FnvHash、SHA 等。
(3)添加元素(Add Element):当向 Bloom 过滤器中添加一个元素时,将该元素经过多个哈希函数的计算得到的位置对应的位设置为1。
(4)查询元素(Query Element):当查询一个元素时,通过多个哈希函数计算出对应的位置,并检查这些位置上的位是否都为1。如果有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中。
Bloom 过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中,并将对应的位设置为1。当查询一个元素时,通过对该元素进行相同的哈希计算,检查对应的位是否都为1。如果其中有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中,但并不确定,存在一定的概率误判。
当有变量被加入集合时,通过N个映射函数将这个变量映射成位图中的N个点, 把它们置为 1(假定有两个变量都通过 3 个映射函数)。查询某个变量的时候我们只要看看这些点是不是都是 1, 就可以大概率知道集合中有没有它了:如果这些点,有任何一个为零则被查询变量一定不在;
如果都是 1,则被查询变量很可能存在(因为存在哈希碰撞的情况)。
(1)常用于预防缓存穿透:其存储已存在数据的key,当有新的请求时,先到布隆过滤器中查询是否存在:不存在则直接返回;已存在,才去查询缓存redis,如果redis里没查询到则再查询Mysql数据库。
(2)另外也可以根据布隆过滤器数据结构的特点,用于进行黑名单、白名单校验等场景。
布隆过滤器有两方面的缺陷,都是由哈希碰撞导致的:(1)如果判断为有,可能为无;
(2)无法删除元素,否则会导致存在的其他元素被删除而判断为无。
在 Redis 中,可以使用 RedisBloom 模块来实现布隆过滤器。RedisBloom 是一个开源模块,提供了一系列命令来操作布隆过滤器。下面是 RedisBloom 模块中常用的命令集合:
(1)BF.ADD:向布隆过滤器中添加一个元素。
BF.ADD <key> <item>
(2)BF.EXISTS:检查一个元素是否存在于布隆过滤器中。
BF.EXISTS <key> <item>
(3)BF.MADD:向布隆过滤器中批量添加多个元素。
BF.MADD <key> <item> [item ...]
(4)BF.MEXISTS:批量检查多个元素是否存在于布隆过滤器中。
BF.MEXISTS <key> <item> [item ...]
(5)BF.INFO:获取布隆过滤器的信息,包括容量、误判率等。
BF.INFO <key>
(6)BF.RESERVE:创建一个新的布隆过滤器,并指定容量和误判率。
BF.RESERVE <key> <error_rate> <capacity>
(7)BF.COUNT:统计布隆过滤器中已添加的元素数量。
BF.COUNT <key>
(8)BF.DEBUG:调试命令,用于打印布隆过滤器内部的一些调试信息。
BF.DEBUG <subcommand> [arguments ...]
- <!--redis的依赖-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.script.DefaultRedisScript;
- import org.springframework.data.redis.core.script.RedisScript;
- import org.springframework.data.redis.serializer.RedisSerializer;
- import org.springframework.stereotype.Component;
- import org.springframework.transaction.annotation.Transactional;
-
- import java.util.Collections;
- import java.util.List;
- import java.util.stream.Collectors;
-
- @Component
- public class RedisBloomUtil {
- @Autowired
- private RedisTemplate redisTemplate;
- // 初始化一个布隆过滤器
- public Boolean tryInitBloomFilter(String key, long expectedInsertions, double falseProbability) {
- Boolean keyExist = redisTemplate.hasKey(key);
- if(keyExist) {
- return false;
- }
- RedisScript<Boolean> script = new DefaultRedisScript<>(bloomInitLua(), Boolean.class);
- RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
- redisTemplate.execute(script, stringSerializer, stringSerializer, Collections.singletonList(key), falseProbability+"", expectedInsertions+"");
- return true;
- }
- // 添加元素
- public Boolean addInBloomFilter(String key, Object arg) {
- RedisScript<Boolean> script = new DefaultRedisScript<>(addInBloomLua(), Boolean.class);
- return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
- }
- @Transactional
- // 批量添加元素
- public Boolean batchAddInBloomFilter(String key, Object... args) {
- RedisScript<Boolean> script = new DefaultRedisScript<>(batchAddInBloomLua(), Boolean.class);
- return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), args);
- }
- // 查看某个元素是否是存在
- public Boolean existInBloomFilter(String key, Object arg) {
- RedisScript<Boolean> script = new DefaultRedisScript<>(existInBloomLua(), Boolean.class);
- return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
- }
- // 批量查看元素是否存在
- public List batchExistInBloomFilter(String key, Object... args) {
- RedisScript<List> script = new DefaultRedisScript(batchExistInBloomLua(), List.class);
- List<Long> results = (List) redisTemplate.execute(script, Collections.singletonList(key), args);
- List<Boolean> booleanList = results.stream().map(res -> res == 1 ? true : false).collect(Collectors.toList());
- return booleanList;
- }
-
-
- private String bloomInitLua() {
- return "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
- }
- private String addInBloomLua() {
- return "return redis.call('bf.add', KEYS[1], ARGV[1])";
- }
- private String batchAddInBloomLua() {
- StringBuilder sb = new StringBuilder();
- sb.append("for index, arg in pairs(ARGV)").append("\r\n");
- sb.append("do").append("\r\n");
- sb.append("redis.call('bf.add', KEYS[1], arg)").append("\r\n");
- sb.append("end").append("\r\n");
- sb.append("return true");
- return sb.toString();
- }
- private String existInBloomLua() {
- return "return redis.call('bf.exists', KEYS[1], ARGV[1])";
- }
- private String batchExistInBloomLua() {
- StringBuilder sb = new StringBuilder();
- sb.append("local results = {}").append("\r\n");
- sb.append("for index, arg in pairs(ARGV)").append("\r\n");
- sb.append("do").append("\r\n");
- sb.append("local exist = redis.call('bf.exists', KEYS[1], arg)").append("\r\n");
- sb.append("table.insert(results, exist)").append("\r\n");
- sb.append("end").append("\r\n");
- sb.append("return results;");
- return sb.toString();
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。