Filtro Bloom em Java usando Goiaba
*1. Visão geral *
Neste artigo, veremos a construção do filtro Bloom da biblioteca Guava. Um filtro Bloom é uma estrutura de dados probabilística com eficiência de memória que podemos usar para responder à pergunta* se um determinado elemento está ou não em um conjunto *.
*Não há falsos negativos* com um filtro Bloom; portanto, quando ele retorna _false_, podemos ter 100% de certeza de que o elemento não está no conjunto.
No entanto, um filtro Bloom pode retornar falsos positivos ; portanto, quando retornar true, há uma alta probabilidade de que o elemento esteja no conjunto, mas não podemos ter 100% de certeza.
Para uma análise mais aprofundada de como um filtro Bloom funciona, você pode acessar este tutorial.
*2. Dependência Maven *
Usaremos a implementação do filtro Bloom do Guava, então vamos adicionar a dependência guava:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
A versão mais recente pode ser encontrada em Maven Central
===* 3. Por que usar o Bloom Filter? *
O filtro Bloom foi* projetado para ser eficiente em termos de espaço e rápido . Ao usá-lo, podemos *especificar a probabilidade de respostas falsas positivas que podemos aceitar e, de acordo com essa configuração, o filtro Bloom ocupará o mínimo de memória possível.
Devido a essa eficiência de espaço, o filtro Bloom caberá facilmente na memória , mesmo para um grande número de elementos. Alguns bancos de dados, incluindo Cassandra e Oracle, usam esse filtro como a primeira verificação antes de ir para o disco ou cache, por exemplo, quando uma solicitação de um ID específico é recebida.
Se o filtro retornar que o ID não está presente, o banco de dados poderá interromper o processamento da solicitação e retornar ao cliente. Caso contrário, ele será direcionado ao disco e retornará o elemento se ele for encontrado no disco.
*4. Criando um filtro Bloom *
Suponha que desejemos criar um filtro Bloom para até 500 Integers e que possamos tolerar uma probabilidade de um por cento (0,01) de falsos positivos.
Podemos usar a classe BloomFilter da biblioteca Guava para conseguir isso. Precisamos passar o número de elementos que esperamos inserir no filtro e a probabilidade de falso positivo positiva desejada:
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
500,
0.01);
Agora vamos adicionar alguns números ao filtro:
filter.put(1);
filter.put(2);
filter.put(3);
Adicionamos apenas três elementos e definimos que o número máximo de inserções será 500, portanto nosso filtro Bloom* deve gerar resultados muito precisos *. Vamos testá-lo usando o método _mightContain () _:
assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
assertThat(filter.mightContain(100)).isFalse();
Como o nome sugere, não podemos ter 100% de certeza de que um determinado elemento esteja realmente no filtro quando o método retornar true.
Quando mightContain () _ retorna _true em nosso exemplo, podemos ter 99% de certeza de que o elemento está no filtro e há uma probabilidade de um por cento de que o resultado seja um falso positivo. Quando o filtro retorna false, podemos ter 100% de certeza de que o elemento não está presente.
*5. Filtro Bloom saturado em excesso *
Quando projetamos nosso filtro Bloom,* é importante fornecer um valor razoavelmente preciso para o número esperado de elementos *. Caso contrário, nosso filtro retornará falsos positivos a uma taxa muito maior que a desejada. Vamos ver um exemplo.
Suponha que criamos um filtro com uma probabilidade falso-positiva desejada de um por cento e esperamos alguns elementos iguais a cinco, mas inserimos 100.000 elementos:
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
5,
0.01);
IntStream.range(0, 100_000).forEach(filter::put);
Como o número esperado de elementos é muito pequeno, o filtro ocupará muito pouca memória.
No entanto, à medida que adicionamos mais itens do que o esperado, o filtro fica saturado demais e tem uma probabilidade muito maior de retornar resultados positivos falsos do que o percentual desejado:
assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
assertThat(filter.mightContain(1_000_000)).isTrue();
Observe que o método mightContatin () _ retornou _true mesmo para um valor que não inserimos