Filtres de Bloom en Go : compromis et mise en œuvre

À pleine charge, 2,16 millions de tests d’appartenance ont mitraillé notre service de reco — 97 % de négatifs qui gaspillaient de l’I/O. Les filtres de Bloom ont tout changé, en écartant les intrus d’entrée de jeu en Go.

Tableau de bits d’un filtre de Bloom montrant collisions de hachage et exemple de faux positif dans un système de recommandation

Key Takeaways

  • Les filtres de Bloom brillent dans les charges ultra-négatives, en taillant l’I/O de 97-98 % en rejetant en mémoire.
  • Ajustez m et k avec précision mathématique pour équilibrer mémoire et précision — fini les approximations.
  • Le contrôle bas niveau de Go simplifie l’implé ; parfait pour des moteurs reco en prod à grande échelle.
  • À éviter pour ensembles denses ou delete-heavy — préférez les hashmaps.
  • Parallèle historique : comme les bitmaps des années 90, Bloom prêt pour l’explosion edge/serverless.

Dix-huit mille requêtes par seconde. Le vacarme qui s’abat sur notre service de flux de recommandations, chacune jonglant avec 120 candidats articles, et forçant plus de deux millions de sondages d’appartenance à chaque seconde folle.

Les filtres de Bloom ont tout révolutionné. Glissez-en un devant vos lookups exacts — surtout quand 97-98 % hurlent « négatif » — et regardez l’I/O backend s’effondrer. On parle d’une latence p95 qui replonge de 140 ms à 85 ms, de pics de lectures domptés, de coûts maîtrisés. Zéro faux négatifs, juste des faux positifs contrôlés à vérifier plus tard. Dubitatif ? Les maths ne mentent pas, et Go rend ça enfantin.

Lookups exacts : parfait pour la correction, l’enfer pour l’échelle

Imaginez le chemin naïf. Générer les candidats. Vérifier chacun contre l’historique utilisateur — cache d’abord, puis stockage. Idéal pour éviter les doublons. Mais avec des négatifs omniprésents ? Vous cramez réseau et disque pour des « non » à répétition.

Le hic : l’éventail des candidats fait exploser le volume de lectures par requête. Pics de trafic ? Le backend craque. Notre service a vu les factures infra grimper alors que les checks exacts étranglaient le chemin critique.

« Pendant les pics de trafic, cela se traduisait par une latence p95 en hausse (de 85 ms à 140 ms environ), des pics de lectures backend et des coûts infra qui ne cessaient de monter. »

Cette citation tape dans le mille — de la part des ingénieurs qui l’ont vécu. L’exact est déterministe. Mais pour des charges ultra-négatives ? C’est comme fouiller une botte de foin pour chaque aiguille qui n’y est pas.

Entrée en lice des filtres de Bloom

Les filtres de Bloom, donc. Tableau de bits. K fonctions de hachage. Insertion : poser les bits. Requête : tous les bits posés ? Possiblement oui (risque de faux positif), tous bons ? Définitivement non.

Pas de faux négatifs — vital pour notre règle « ne pas resservir les articles vus ». Rater un positif ? Catastrophe. Des extras ? Vérification en aval, assurance bon marché.

Nouveau parcours : Hacher (user_id, article_id). Filtre dit non ? Garder le candidat, pas vu. Dit peut-être ? Check exact. Boum — 98 % sautent l’étape coûteuse.

Latence en chute. Lectures en vrille. Coûts stabilisés. Victoire data-driven.

Ajuster les molettes : des maths qui comptent

Pas d’approximation. Les paramètres dictent tout : m (taille du tableau de bits), k (hachages), n (insertions prévues).

Faux positif optimal p ? Environ (0,6185)^{m/n}. Ajustez m = -n ln p / (ln 2)^2, k = (m/n) ln 2. Pour notre échelle — des millions de paires uniques (user, article) — montez jusqu’à des gigaoctets si besoin, mais shardez ou rafraîchissez malin.

Le truc : m trop petit ? Faux positifs qui explosent, lookups exacts qui s’asphyxient. Trop de k ? Mémoire qui gonfle, hachages plus lents. Le syso.MemStats() de Go aide à surveiller.

On a choisi conservateur : cible 1 % de faux positifs. Ça rentre en heap. En prod ? Rotation horaire des filtres, rebuild depuis les logs. Paradis des trade-offs.

Pourquoi les filtres de Bloom écrasent les lookups exacts en charges négatives ?

Les données hurlent oui. 97 % de négatifs ? Vous optimisiez pour le mauvais cas. Bloom inverse : en mémoire pour le chemin courant (non), remote seulement pour les raretés (oui).

Comparé aux caches seuls. LRU vire les hot, les misses tapent encore le stockage. Bloom ? Statique, probabiliste, pas de drame d’éviction. Et moins cher que des sets in-mem complets pour des ensembles clairsemés.

Mais attends, du buzz ? Que nenni. On l’a déjà vu. Les index bitmap d’Oracle dans les bases des années 90 faisaient pareils pour tailler les queries. Bloom, c’est l’évolution moderne à base de hachage. Mon avis : à l’ère serverless où les cold starts tuent, le retour en force de Bloom cogne les caches edge. Pari osé : dans tous les Lambda d’ici 2026.

Para court pour l’impact : Ça scale.

Plus profond : Go excelle là-dedans. Pas de pauses GC qui ruinent la late

Sarah Chen
Written by

AI research editor covering LLMs, benchmarks, and the race between frontier labs. Previously at MIT CSAIL.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by InfoQ