Explicación detallada del uso del filtro Bloom en redis


1. Introducción al filtro Bloom

1. ¿Qué es un filtro Bloom?

布隆过滤器(英语:Bloom Filter)Fue propuesto por Bloom en 1970. En realidad, es un vector binario largo y una serie de funciones de mapeo aleatorio. principalmente 用于判断一个元素是否在一个集合中.

Por lo general, nos encontraremos con muchos escenarios comerciales en los que necesitamos juzgar si un elemento está en una determinada colección. La idea general es guardar todos los elementos de la colección y luego determinar mediante comparación. La lista vinculada, el árbol, la tabla hash (también llamada tabla hash, tabla hash) y otras estructuras de datos son todas de esta manera. Pero 随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logN),O(1).

En ese momento, nació el Bloom Filter (Bloom Filter).

2. Principio de implementación del filtro Bloom.

Si desea juzgar si un elemento está en una colección, el método general que le viene a la mente es almacenar temporalmente los datos y luego buscar para determinar si existe en la colección. Este método es aplicable cuando la cantidad de datos es relativamente pequeña, pero cuando hay muchos elementos en unos pocos, la velocidad de recuperación será cada vez más lenta.

Se puede utilizar un mapa de bits: simplemente verifique si el punto correspondiente es 1 para saber si existe dicho número en el conjunto. El filtro Bloom puede considerarse como una extensión del mapa de bits, pero utiliza múltiples funciones de mapeo hash para reducir la probabilidad de colisiones hash.

El algoritmo es como sigue:
inserte la descripción de la imagen aquí

BloomFilter se compone de un vector binario o mapa de bits (mapa de bits) de tamaño fijo y una serie de funciones de mapeo.

  1. En el estado inicial, para una matriz de bits de longitud m, todos sus bits se establecen en 0;
  2. Cuando se agrega una variable al conjunto, la variable se asigna a K puntos en el mapa de bits mediante K funciones de mapeo y se establecen en 1;
  3. Al consultar si existe una variable, solo necesitamos verificar si estos puntos son todos 1 para saber si existe en el conjunto con una alta probabilidad.
  • Si alguno de estos puntos tiene 0, la variable consultada no debe estar allí;
  • Si ambos son 1, se consulta la variable 很可能存在.
    ¿Por qué decimos que puede existir, pero no necesariamente existir? Esto se debe a que la función de mapeo en sí es una función hash y la función hash tendrá colisiones.

3. Tasa de errores de juicio

La tasa de errores de juicio aquí se refiere a BloomFilter 判断某个 key 存在,但它实际不存在的概率.

El error de juicio del filtro Bloom se debe al hecho de que varias entradas se procesan en la misma posición de bit, por lo que es imposible determinar qué entrada se genera, por lo que la causa principal del error de juicio es que el mismo bit se asigna varias veces. y ponerlo en 1.

La función hash en sí tendrá colisiones. Aunque el filtro Bloom utilizará múltiples cálculos hash para reducir la probabilidad de colisiones, no se puede evitar por completo. Esto hará que los bits de un objeto que se han sometido a múltiples cálculos hash se superpongan con los bits de otros objetos. Si los bits de un nuevo objeto se establecen en 1 cuando se almacenan en otros objetos, se producirá un error de juicio.

Esta situación también causó el problema de eliminación del filtro Bloom, porque 布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位. Si eliminamos este bit directamente, afectará a otros elementos.

Diagrama de error de juicio del filtro Bloom:
1. Estado inicial: la matriz de bits en el filtro Bloom es 0.
2. Agregue el objeto X y el objeto Y al filtro Bloom, y después de múltiples cálculos hash, la matriz de bits 1, 2, 4, 5, y 7 se llenan con 1.
3. Cuando se usa el filtro Bloom para determinar si el objeto Z existe, primero se realizan múltiples cálculos hash en el objeto Z y los bits correspondientes son 4, 5 y 7. Sin embargo, 4, 5 y 7 acaban de ser llenados con 1 por el objeto X y el objeto Y. En este momento, se juzgará erróneamente que el objeto Z ya existe.
4. En casos extremos, cuando todos los bits de la matriz de bits se establecen en 1, el filtro Bloom fallará.
inserte la descripción de la imagen aquí

característica:

  • Un elemento no existe necesariamente si el resultado del juicio es existencia, pero no debe existir cuando el resultado del juicio es inexistencia.
  • Los filtros Bloom pueden agregar elementos, pero no pueden eliminarlos, porque eliminar elementos aumentará la tasa de falsos positivos.

¿Cómo reducir la tasa de errores de juicio?

  1. Aumente la cantidad de funciones hash para reducir la posibilidad de colisiones de bits;
  2. Aumente el tamaño del mapa de bits para evitar una gran cantidad de bits cubiertos y rellenos.

4. Escenarios de uso del filtro Bloom

Las aplicaciones típicas de los filtros Bloom son:

  • 数据库防止穿库, Google Bigtable, HBase y Cassandra, y Postgresql usan BloomFilter para reducir las búsquedas en disco de filas o columnas que no existen. Evitar costosas búsquedas en disco puede mejorar en gran medida el rendimiento de las operaciones de consulta de bases de datos.
  • En un escenario empresarial, juzgar si un usuario ha leído un determinado vídeo o artículo, como Douyin o Toutiao, por supuesto dará lugar a ciertos errores de juicio, pero no permitirá que los usuarios vean contenido duplicado.
  • 缓存宕机、缓存击穿场景, generalmente juzga si el usuario está en el caché, si es así, el resultado se devolverá directamente, si no, se consultará la base de datos, si llega una ola de datos fríos, provocará una gran cantidad de fallas en el caché, lo que provocará un efecto de avalancha, en este momento puede usar el filtro Bloom como caché. El índice, solo en el filtro Bloom, se usa para consultar el caché, y si no se encuentra, se penetra en la base de datos. Si no está en el bombacho, regresa directamente.
  • WEB拦截器, si la solicitud es la misma, será interceptada para evitar ataques repetidos. Cuando el usuario solicita por primera vez, coloque los parámetros de la solicitud en el filtro Bloom, y cuando el usuario realiza la segunda solicitud, primero juzgue si el filtro Bloom afecta los parámetros de la solicitud. Puede mejorar la tasa de aciertos de caché.
  • El servidor de caché proxy web Squid utiliza filtros Bloom en resúmenes de caché. Google Chrome utiliza filtros Bloom para acelerar la navegación segura

En general, 布隆过滤器是用于大数据场景下的重复判断,并且允许有一定误差存在el uso más típico es resolver el problema de penetración de caché.

5. Comparación entre la tabla hash y el filtro Bloom

Las tablas hash también se pueden usar para determinar si un elemento está en un conjunto, pero Bloom Filter solo necesita 1/8 o 1/4 de la complejidad espacial de una tabla hash para completar el mismo problema.
La tabla hash almacena los elementos reales en la colección, mientras que el filtro Bloom solo llena la matriz binaria de acuerdo con los resultados del cálculo hash múltiple de los elementos y no almacena los objetos reales.

Bloom Filter puede insertar elementos, pero no puede eliminar elementos existentes. Cuantos más elementos haya en el conjunto, mayor será la tasa de falsos positivos, pero no habrá falsos negativos.

2. Combate real del filtro Bloom en redis

"Sobre el papel, siempre es superficial, pero sé que este asunto debe resolverse". A continuación, veamos cómo evitar el problema de penetración de caché al consultar información de pedidos a través del filtro Bloom.
inserte la descripción de la imagen aquí

1. Introducir la dependencia de Redisson

  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson-spring-boot-starter</artifactId>
      <version>3.16.7</version>
  </dependency>

2. Crea una tabla de pedidos

CREATE TABLE `tb_order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单Id',
  `order_desc` varchar(50) NOT NULL COMMENT '订单描述',
  `user_id` bigint NOT NULL COMMENT '用户Id',
  `product_id` bigint NOT NULL COMMENT '商品Id',
  `product_num` int NOT NULL COMMENT '商品数量',
  `total_account` decimal(10,2) NOT NULL COMMENT '订单金额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `ik_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

3. Configurar Redis

Se agregaron propiedades de conexión de Redis:

spring.redis.host=192.168.206.129
spring.redis.port=6379
spring.redis.password=123456

Configure redisTemplate, principalmente para establecer la estrategia de serialización Jackson

@Configuration
public class RedisConfig {
    
    

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redisConnectionFactory) {
    
    
        //设置序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //反序列化,该设置不能省略,不然从redis获取json转为实体时会报错
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        //key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        //value序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }

}

4. Coloque BloomFilter

/**
 * 配置布隆过滤器
 */
@Configuration
public class BloomFilterConfig {
    
    
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 创建订单号布隆过滤器
     * @return
     */
    @Bean
    public RBloomFilter<Long> orderBloomFilter() {
    
    
        //过滤器名称
        String filterName = "orderBloomFilter";
        // 预期插入数量
        long expectedInsertions = 10000L;
        // 错误比率
        double falseProbability = 0.01;
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(filterName);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
        return bloomFilter;
    }
}

5. Crear orden

BloomFilter en Redisson tiene 2 métodos principales:

  • BloomFilter.add(orderId) Agregar ID al filtro de floración
  • BloomFilter.contains (orderId) Determina si la identificación existe
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    
    

    @Resource
    private RBloomFilter<Long> orderBloomFilter;

    @Resource
    private TbOrderMapper  tbOrderMapper;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    @Override
    public void createOrder(TbOrder tbOrder) {
    
    
        //1、创建订单
        tbOrderMapper.insert(tbOrder);

        //2、订单id保存到布隆过滤器
        log.info("布隆过滤器中添加订单号:{}",tbOrder.getId());
        orderBloomFilter.add(tbOrder.getId());
    }

    @Override
    public TbOrder get(Long orderId) {
    
    
        TbOrder tbOrder = null;
        //1、根据布隆过滤器判断订单号是否存在
        if(orderBloomFilter.contains(orderId)){
    
    
            log.info("布隆过滤器判断订单号{}存在",orderId);
            String key = "order:"+orderId;
            //2、先查询缓存
            Object object = redisTemplate.opsForValue().get(key);
            if(object != null){
    
    
                log.info("命中缓存");
                tbOrder =  (TbOrder)object;
            }else{
    
    
                //3、缓存不存在则查询数据库
                log.info("未命中缓存,查询数据库");
                tbOrder = tbOrderMapper.selectById(orderId);
                redisTemplate.opsForValue().set(key,tbOrder);
            }
        }else{
    
    
            log.info("判定订单号{}不存在,不进行查询",orderId);
        }
        return tbOrder;
    }
}

6. Pruebas unitarias

    @Test
    public void testCreateOrder() {
    
    
        for (int i = 0; i < 50; i++) {
    
    
            TbOrder tbOrder = new TbOrder();
            tbOrder.setOrderDesc("测试订单"+(i+1));
            tbOrder.setUserId(1958L);
            tbOrder.setProductId(102589L);
            tbOrder.setProductNum(5);
            tbOrder.setTotalAccount(new BigDecimal("300"));
            tbOrder.setCreateTime(new Date());
            orderService.createOrder(tbOrder);
        }
    }

    @Test
    public void testGetOrder() {
    
    
        TbOrder  tbOrder = orderService.get(25L);
        log.info("查询结果:{}", tbOrder.toString());
    }

Resumir

El principio del filtro Bloom es en realidad muy simple, es decir, mapa de bits + múltiples hashes. La principal ventaja es que puede determinar rápidamente si un objeto existe en datos a gran escala usando un espacio muy pequeño. La desventaja es que hay un posibilidad de error de juicio, pero no Se perderá el juicio, es decir, definitivamente se considerará que el objeto existente existe, y el objeto inexistente tendrá una menor probabilidad de ser juzgado erróneamente como existente, y la eliminación del objeto no es apoyada, porque la probabilidad de error de juicio aumentará. El uso más típico es el de resolver 缓存穿透problemas.

Supongo que te gusta

Origin blog.csdn.net/w1014074794/article/details/129750865
Recomendado
Clasificación