Tout d'abord, le filtre Bloom Profil
La dernière fois que nous avons appris à utiliser Hyperloglog une large estimation des données, il est très précieux, peut résoudre beaucoup de précision statistique n'est pas une forte demande. Mais si nous voulons connaître l' une des valeurs est pas déjà Hyperloglog à l' intérieur de la structure, il ne fera rien, il ne fournit que pfadd
et les pfcount
méthodes ne fournissent pas la même contains
méthode.
Permettez - moi de donner une scène, comme vous brossez vibrato :
Vous avez à la brosse grâce à des recommandations répétées faire? Donc , beaucoup de contenu recommandé à recommander à tant d'utilisateurs, il est de savoir comment faire en sorte que chaque utilisateur tout en regardant le contenu recommandé, recommandé de veiller à ce que la vidéo ne semble pas before've vu? En d' autres termes, le vibrato est de savoir comment parvenir à pousser envoyé à re - faire?
Vous penseriez le serveur enregistre l'utilisateur Lire toute l'histoire , ce sera de l'histoire du système de chaque utilisateur lorsque la recommandation recommandée courte vidéo de dépistage , filtrer les enregistrements qui existent déjà. Le problème est que quand un grand nombre d'utilisateurs , chaque utilisateur a regardé une courte vidéo et beaucoup de cas, de cette façon, le système de recommandation re-travail en termes de performance avec sur elle?
En fait, si l' histoire est stockée dans une base de données relationnelle, vous avez besoin d'aller lourds sur la base de données fréquemment exists
Renseignez-, lorsque le système de haute concurrence, la base de données est la pression difficile résisté aux.
Vous pouvez également pensé cache , mais tant d'utilisateurs tant d' histoire, si mises en cache tout ce qui était nécessaire à gaspiller beaucoup d' espace ah .. (patron peut regarder le projet de loi, regardez - vous ..) et cet espace de stockage sera augmente de façon linéaire avec le temps, même si vous vivez un mois avec un cache peut le faire, mais combien de temps il peut continuer à tenir? Ne pas les performances du cache ne peut pas suivre, nous censés faire?
Comme on le voit ci - dessus, le filtre de Bloom (Bloom Filter) est un de ces structures de données de haut niveau visant à résoudre le problème de la dé-duplication. Mais avec Hyperloglog , comme il l' a aussi un peu inexact, il y a une certaine probabilité d'erreur judiciaire, mais il peut en même temps pour résoudre le lourd, dans l'espace permet d' économiser 90% ou plus, est très utile.
Quel est le filtre Bloom
filtre de Bloom (Bloom Filter) en 1970 proposé par Bloom. Il est en fait une très longue série de vecteur binaire aléatoire et la fonction de cartographie (voir les détails ci - dessous) , en fait, vous pouvez le mettre simplement compris comme très précis ensemble la structure, lorsque vous utilisez la contains
méthode de détermination d' un objet s'il y a, il peut méjuger. Cependant, le filtre Bloom est pas particulièrement précis, tant qu'un ensemble raisonnable de paramètres, il est une précision relativement suffisante peut être contrôlée avec précision, il ne sera qu'une faible probabilité de faux positifs.
Lorsque le mot de filtre Bloom qu'il ya une valeur qui ne peut pas exister , quand il dit là, il n'existe certainement pas . Analogie, quand il le fait ne sait pas vraiment, mais vous savez que vous reconnaît pas, quand il est temps de dire, probablement parce que vous savez qu'il ressemble à un autre ami (ressemble un peu similaire face) , donc vous connaissez une erreur judiciaire.
Bloom scénarios d'utilisation du filtre
Sur la base des caractéristiques ci-dessus, nous pouvons mettre sensiblement dans un filtre Bloom pour les scénarios suivants:
-
Big données pour déterminer s'il y a : Il peut atteindre mentionné ci-dessus de déduplication fonction, si la mémoire de votre serveur est assez grand, puis utiliser un HashMap peut être une bonne solution, en théorie, la complexité temporelle de O niveaux peut atteindre (1, mais quand la quantité de données ensemble, ou considérer que le filtre Bloom.
-
Cache résolution pénétration : Nous allons souvent mettre des données à chaud dans Redis en cache, comme les détails de ce produit. Nous venons généralement après une demande sera d' abord de cache de requête, sans lire directement la base de données, ce qui est d'améliorer les performances de la pratique la plus simple et la plus courante, mais si la demande a un cache qui n'existe pas , le cache ne doit pas exister à ce moment, il il y aura un grand nombre de demandes a frappé directement la base de données , la résultante pénétration tampon , filtre Bloom peut également être utilisé pour résoudre de tels problèmes.
-
reptiles / mail et d' autres systèmes de filtrage : ne savent généralement pas si vous jamais remarqué à quel point certains des spams de courrier normal sera mis dans le répertoire, qui utilise le filtre Bloom mauvais jugement causé.
En second lieu, le principe du filtre Bloom à résoudre
filtre de Bloom essentiellement par la longueur du m
vecteur ou d'une liste de bits binaire (contenant uniquement 0
ou une 1
liste de valeurs de bits), avec les valeurs initiales sont tous ensemble 0
, de sorte que nous créons d' abord un vecteur de bits légèrement plus utilisé pour l'affichage :
Lorsque l' on ajoute aux données du filtre Bloom, utilise une pluralité de hash
fonction key
calcule, la valeur de l' indice calculé d'un certificat, le nombre de bits d'opération modulo longueur pour obtenir une position, chaque hash
fonction sera considérée comme un autre emplacement. Ce groupe de plusieurs endroits et le nombre de bits sont à 1
terminer l' add
opération, par exemple, on ajoute un wmyskxz
:
requêtes de recherche de filtre de Bloom à key
savoir s'il y a, avec add
la même opération, mettra ce à key
travers une pluralité de la même hash
fonction de conduite des opérations, voir position correspondant à savoir si tout est 1
, tant qu'il y est un peu 0
, puis dit filtre Ming Bulong qui key
ne existent. Si ces endroits sont 1
, cela ne signifie pas qu'il key
doit y avoir, ne peut dire qu'il n'y a plus probablement parce que ces positions 1
peuvent être parce que l'autre key
existe en raison.
Par exemple, nous venons de add
certaines données après une requête n'existe pas dans key
:
De toute évidence, 1/3/5
ces positions 1
parce que d' abord ajouté ci - dessus wmyskxz
fait, donc là , il y a une erreur judiciaire . Heureusement, il y a un filtre Bloom peut prédire la formule de taux de faux positifs, plus complexe, et les amis intéressés peuvent aller lire eux - mêmes, la combustion relativement cerveau .. juste besoin de se rappeler des points suivants comme:
-
Lorsque vous utilisez ne laissez pas le nombre réel est beaucoup plus grande que le nombre d'éléments dans l'initialisation ;
-
Lorsque le nombre d'éléments est supérieur au nombre d'initialisation du filtre de Bloom doit être reconstruit et ré-allouer un
size
plus grand filtre, puis tous les éléments historiques du lotadd
effectués;
En troisième lieu, le filtre utilisé Bloom
REDIS officiel filtre Bloom fourni au Redis 4,0 avant le début officiel après la fourniture d' un plug-in fonction. filtre de Bloom est chargé comme un plug-in de Redis Server fournit une fonctionnalité de déduplication puissant Bloom à Redis. L'expérience Let le filtre Bloom Redis 4.0, ce qui élimine la nécessité d' une lourdeur processus d'installation, nous directement Docker il.
> docker pull redislabs/rebloom # 拉取镜像
> docker run -p6379:6379 redislabs/rebloom # 运行容器
> redis-cli # 连接容器中的 redis 服务
Si les trois instructions ci-dessus pour effectuer aucun problème, ici vous pouvez éprouver le filtre Bloom.
-
Bien sûr, si vous ne voulez pas utiliser Docker, peut également être installé dans la machine après avoir vérifié les versions Redis qualifiée sur leurs propres plug-ins, peuvent être trouvés ici: https://blog.csdn.net/u013030276/article/details/88350641
Utilisation de base du filtre Bloom
Filtre Bloom Il y a deux commandes de base, bf.add
éléments add, bf.exists
requête si l'élément existe, son utilisation et définir l'ensemble sadd
et sismember
les mêmes. Notez que bf.add
seulement ajouter un élément, si vous voulez ajouter plus, vous devez utiliser la bf.madd
commande. De même , si vous avez besoin d' une requête s'il y a des éléments multiples, il faut utiliser la bf.mexists
commande.
127.0.0.1:6379> bf.add codehole user1
(integer) 1
127.0.0.1:6379> bf.add codehole user2
(integer) 1
127.0.0.1:6379> bf.add codehole user3
(integer) 1
127.0.0.1:6379> bf.exists codehole user1
(integer) 1
127.0.0.1:6379> bf.exists codehole user2
(integer) 1
127.0.0.1:6379> bf.exists codehole user3
(integer) 1
127.0.0.1:6379> bf.exists codehole user4
(integer) 0
127.0.0.1:6379> bf.madd codehole user4 user5 user6
1) (integer) 1
2) (integer) 1
3) (integer) 1
127.0.0.1:6379> bf.mexists codehole user4 user5 user6 user7
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 0
Bloom à travers le filtre utilisé ci - dessus ne sont que les paramètres par défaut du filtre Bloom, il est la première fois que nous add
créons automatiquement le temps. Redis peut également fournir des paramètres personnalisés filtre Bloom, seulement besoin d' add
utiliser l'avant bf.reserve
instruction explicitement créer assez. Si le correspondant key
existe déjà une bf.reserve
erreur.
bf.reserve
Il y a trois paramètres, à savoir key
, error_rate
(erreur taux) et initial_size
:
-
error_rate
La partie inférieure, plus d' espace est nécessaire , ne l'expérience globale de l' affichage ou non ne pas être trop précis pour l'occasion, mettre un peu plus grande n'a pas d' importance, comme le système de poussée au- dessus dudit, ne fera qu'une petite partie du contenu est filtré, il sera grandement affectée; -
initial_size
A dit qu'il prévoit que le nombre d'éléments chargés , lorsque le nombre réel dépasse cette valeur, le taux de faux positifs augmentera, il est donc nécessaire de mettre en avant une grande valeur à ne pas dépasser le taux de faux positifs dans élevé;
Si non applicable bf.reserve
, par défaut error_rate
Shi 0.01
, la valeur par défaut initial_size
Shi 100
.
En quatrième lieu, le filtre Bloom code mis en œuvre
Propre implémentation simple analogique
Selon la théorie de base de ce qui précède, nous pouvons facilement mettre en œuvre leur propre pour une 简单模拟
structure de données de filtre Bloom:
public static class BloomFilter {
private byte[] data;
public BloomFilter(int initSize) {
this.data = new byte[initSize * 2]; // 默认创建大小 * 2 的空间
}
public void add(int key) {
int location1 = Math.abs(hash1(key) % data.length);
int location2 = Math.abs(hash2(key) % data.length);
int location3 = Math.abs(hash3(key) % data.length);
data[location1] = data[location2] = data[location3] = 1;
}
public boolean contains(int key) {
int location1 = Math.abs(hash1(key) % data.length);
int location2 = Math.abs(hash2(key) % data.length);
int location3 = Math.abs(hash3(key) % data.length);
return data[location1] * data[location2] * data[location3] == 1;
}
private int hash1(Integer key) {
return key.hashCode();
}
private int hash2(Integer key) {
int hashCode = key.hashCode();
return hashCode ^ (hashCode >>> 3);
}
private int hash3(Integer key) {
int hashCode = key.hashCode();
return hashCode ^ (hashCode >>> 16);
}
}
Ici , il est très simple, seul interne maintient un byte
type de data
tableau, en fait, byte
occupe encore un octet autant que peut être optimisé pour bit
être remplacé, ce n'est que pour la simulation de commodité. De plus, j'ai aussi créé trois différentes hash
fonctions, en fait, est le dessin HashMap
façon gigue de hachage, respectivement, en utilisant son propre hash
et différent de la médiane à droite ou à des résultats différents. Et il fournit de base add
et des contains
méthodes.
Le regard Let comment ce simple test effet de filtre Bloom:
public static void main(String[] args) {
Random random = new Random();
// 假设我们的数据有 1 百万
int size = 1_000_000;
// 用一个数据结构保存一下所有实际存在的值
LinkedList<Integer> existentNumbers = new LinkedList<>();
BloomFilter bloomFilter = new BloomFilter(size);
for (int i = 0; i < size; i++) {
int randomKey = random.nextInt();
existentNumbers.add(randomKey);
bloomFilter.add(randomKey);
}
// 验证已存在的数是否都存在
AtomicInteger count = new AtomicInteger();
AtomicInteger finalCount = count;
existentNumbers.forEach(number -> {
if (bloomFilter.contains(number)) {
finalCount.incrementAndGet();
}
});
System.out.printf("实际的数据量:%d, 判断存在的数据量: %d \n", size, count.get());
// 验证10个不存在的数
count = new AtomicInteger();
while (count.get() < 10) {
int key = random.nextInt();
if (existentNumbers.contains(key)) {
continue;
} else {
// 这里一定是不存在的数
System.out.println(bloomFilter.contains(key));
count.incrementAndGet();
}
}
}
La sortie est la suivante:
实际的数据量:1000000, 判断存在的数据量: 1000000
false
true
false
true
true
true
false
false
true
false
Ceci est dit avant, lorsque le filtre Bloom, ladite certaine valeur existe , la valeur peut ne pas exister , quand il a dit qu'une valeur n'existe pas , il serait certainement pas exister , et il y a un certain taux de faux positifs. ..
manuel de référence de mise en œuvre
Bien sûr, la version ci-dessus est particulièrement faible, mais l'idée principale est pas mal, il est également donné une meilleure version de lui-même réalisé un test de référence:
import java.util.BitSet;
public class MyBloomFilter {
/**
* 位数组的大小
*/
private static final int DEFAULT_SIZE = 2 << 24;
/**
* 通过这个数组可以创建 6 个不同的哈希函数
*/
private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};
/**
* 位数组。数组中的元素只能是 0 或者 1
*/
private BitSet bits = new BitSet(DEFAULT_SIZE);
/**
* 存放包含 hash 函数的类的数组
*/
private SimpleHash[] func = new SimpleHash[SEEDS.length];
/**
* 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样
*/
public MyBloomFilter() {
// 初始化多个不同的 Hash 函数
for (int i = 0; i < SEEDS.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
}
}
/**
* 添加元素到位数组
*/
public void add(Object value) {
for (SimpleHash f : func) {
bits.set(f.hash(value), true);
}
}
/**
* 判断指定元素是否存在于位数组
*/
public boolean contains(Object value) {
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
/**
* 静态内部类。用于 hash 操作!
*/
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
/**
* 计算 hash 值
*/
public int hash(Object value) {
int h;
return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
}
}
}
Goyave utiliser Google open source est livré avec le filtre Bloom
Atteint leur but est surtout de mieux vous connaître le principe du filtre Bloom, goyave mis en œuvre le filtre Bloom est relativement autoritaire, de sorte que le projet actuel, nous ne met pas en œuvre besoin manuellement un filtre Bloom.
Tout d'abord, nous devons introduire la dépendance goyave dans le projet:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
En fait, utilisé comme suit:
Nous avons créé un magasin plus jusqu'à 1500 entier filtre Bloom, et nous pouvons tolérer la probabilité pour cent erreur de jugement (0,01)
// 创建布隆过滤器对象
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
1500,
0.01);
// 判断指定元素是否存在
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
// 将元素添加进布隆过滤器
filter.put(1);
filter.put(2);
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
Dans notre exemple, lorsque mightContain()
la méthode retourne true
, nous pouvons 99% déterminé que l'élément filtrant lorsque le filtre est retourné false
, nous pouvons 100% déterminer que l'élément n'est pas présent dans le filtre.
Mettre en oeuvre le filtre Bloom fourni Goyave est encore très bon (Vous voulez en savoir plus à ce sujet peut regarder le code source pour atteindre) , mais il a un défaut majeur est la seule utilisation autonome (En outre, l'expansion de la capacité n'est pas facile) , et maintenant l'Internet sont généralement des scènes distribuées. Pour résoudre ce problème, nous devons utiliser Redis filtre Bloom dans le.
Source: Je n'ai pas trois coeur