当前位置:   article > 正文

布隆过滤器_bloom过滤器

bloom过滤器

一、介绍

1、布隆过滤器介绍

Bloom 过滤器是一种概率型数据结构,用于快速判断一个元素是否属于一个集合。它以较小的空间占用和高效的查询时间著称。其具有判断为无则不存在,判断为有则不一定存在的特性。

2、结构和操作

(1)位数组(Bit Array):Bloom 过滤器使用一个固定长度的位数组来表示集合,并初始化为全0。每个元素通过多个哈希函数映射到位数组上的多个位置。
(2)哈希函数(Hash Function):Bloom 过滤器使用多个独立的哈希函数,每个哈希函数可以将一个元素映射到位数组的不同位置。常用的哈希函数包括 MurmurHash、FnvHash、SHA 等。
(3)添加元素(Add Element):当向 Bloom 过滤器中添加一个元素时,将该元素经过多个哈希函数的计算得到的位置对应的位设置为1。
(4)查询元素(Query Element):当查询一个元素时,通过多个哈希函数计算出对应的位置,并检查这些位置上的位是否都为1。如果有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中。

3、使用:

Bloom 过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中,并将对应的位设置为1。当查询一个元素时,通过对该元素进行相同的哈希计算,检查对应的位是否都为1。如果其中有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中,但并不确定,存在一定的概率误判。

4、思想

当有变量被加入集合时,通过N个映射函数将这个变量映射成位图中的N个点, 把它们置为 1(假定有两个变量都通过 3 个映射函数)。查询某个变量的时候我们只要看看这些点是不是都是 1, 就可以大概率知道集合中有没有它了:如果这些点,有任何一个为零则被查询变量一定不在;
如果都是 1,则被查询变量很可能存在(因为存在哈希碰撞的情况)。

5、使用场景:

(1)常用于预防缓存穿透:其存储已存在数据的key,当有新的请求时,先到布隆过滤器中查询是否存在:不存在则直接返回;已存在,才去查询缓存redis,如果redis里没查询到则再查询Mysql数据库。

(2)另外也可以根据布隆过滤器数据结构的特点,用于进行黑名单、白名单校验等场景。

6、缺点

布隆过滤器有两方面的缺陷,都是由哈希碰撞导致的:(1)如果判断为有,可能为无;

(2)无法删除元素,否则会导致存在的其他元素被删除而判断为无。

7、基础命令

在 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 ...]

 

二、demo

  1. <!--redis的依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>
  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.data.redis.core.script.DefaultRedisScript;
  4. import org.springframework.data.redis.core.script.RedisScript;
  5. import org.springframework.data.redis.serializer.RedisSerializer;
  6. import org.springframework.stereotype.Component;
  7. import org.springframework.transaction.annotation.Transactional;
  8. import java.util.Collections;
  9. import java.util.List;
  10. import java.util.stream.Collectors;
  11. @Component
  12. public class RedisBloomUtil {
  13. @Autowired
  14. private RedisTemplate redisTemplate;
  15. // 初始化一个布隆过滤器
  16. public Boolean tryInitBloomFilter(String key, long expectedInsertions, double falseProbability) {
  17. Boolean keyExist = redisTemplate.hasKey(key);
  18. if(keyExist) {
  19. return false;
  20. }
  21. RedisScript<Boolean> script = new DefaultRedisScript<>(bloomInitLua(), Boolean.class);
  22. RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
  23. redisTemplate.execute(script, stringSerializer, stringSerializer, Collections.singletonList(key), falseProbability+"", expectedInsertions+"");
  24. return true;
  25. }
  26. // 添加元素
  27. public Boolean addInBloomFilter(String key, Object arg) {
  28. RedisScript<Boolean> script = new DefaultRedisScript<>(addInBloomLua(), Boolean.class);
  29. return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
  30. }
  31. @Transactional
  32. // 批量添加元素
  33. public Boolean batchAddInBloomFilter(String key, Object... args) {
  34. RedisScript<Boolean> script = new DefaultRedisScript<>(batchAddInBloomLua(), Boolean.class);
  35. return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), args);
  36. }
  37. // 查看某个元素是否是存在
  38. public Boolean existInBloomFilter(String key, Object arg) {
  39. RedisScript<Boolean> script = new DefaultRedisScript<>(existInBloomLua(), Boolean.class);
  40. return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
  41. }
  42. // 批量查看元素是否存在
  43. public List batchExistInBloomFilter(String key, Object... args) {
  44. RedisScript<List> script = new DefaultRedisScript(batchExistInBloomLua(), List.class);
  45. List<Long> results = (List) redisTemplate.execute(script, Collections.singletonList(key), args);
  46. List<Boolean> booleanList = results.stream().map(res -> res == 1 ? true : false).collect(Collectors.toList());
  47. return booleanList;
  48. }
  49. private String bloomInitLua() {
  50. return "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
  51. }
  52. private String addInBloomLua() {
  53. return "return redis.call('bf.add', KEYS[1], ARGV[1])";
  54. }
  55. private String batchAddInBloomLua() {
  56. StringBuilder sb = new StringBuilder();
  57. sb.append("for index, arg in pairs(ARGV)").append("\r\n");
  58. sb.append("do").append("\r\n");
  59. sb.append("redis.call('bf.add', KEYS[1], arg)").append("\r\n");
  60. sb.append("end").append("\r\n");
  61. sb.append("return true");
  62. return sb.toString();
  63. }
  64. private String existInBloomLua() {
  65. return "return redis.call('bf.exists', KEYS[1], ARGV[1])";
  66. }
  67. private String batchExistInBloomLua() {
  68. StringBuilder sb = new StringBuilder();
  69. sb.append("local results = {}").append("\r\n");
  70. sb.append("for index, arg in pairs(ARGV)").append("\r\n");
  71. sb.append("do").append("\r\n");
  72. sb.append("local exist = redis.call('bf.exists', KEYS[1], arg)").append("\r\n");
  73. sb.append("table.insert(results, exist)").append("\r\n");
  74. sb.append("end").append("\r\n");
  75. sb.append("return results;");
  76. return sb.toString();
  77. }
  78. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号