REDIS fonctions avancées de cent millions de données de filtrage et filtre Bloom

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 lot  add 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

Publié 277 articles originaux · Praise gagné 65 · vues 380 000 +

Je suppose que tu aimes

Origine blog.csdn.net/ailiandeziwei/article/details/104850563
conseillé
Classement