Le flot de données [veille]

le

Le monde se résume à un gros tas de données, un peu de chiffres, beaucoup de texte. Tel est le principe des Big Datas. Il existe deux types de données : les statiques et les mobiles. Paradoxalement, on s'autorise à modifier les données statiques alors que l'on considère immuable un événement, une modification étant considéré comme un nouvel événement qui amende le précédent. Les événements peuvent faire évoluer un état, et ainsi constituer des données statiques. Une ligne dans un log, un tweet, une opération boursière, une commande sql sont des exemples d'événements.

Journalisation

Un des usages les plus simple de ce principe est la journalisation : on enregistre de manière simple les événements, les uns à la suite des autres, puis de temps en temps, on consolide ces informations, ce qui est une action plus couteuse. C'est comme ça que fonctionne la journalisation des systèmes de fichiers, ou même des bases de données. Ces mêmes bases utilisent une variante pour la réplication sur une autre machine : les événements sont transférés au fur et à mesure sur une autre machine qui va les appliquer sur son propre référentiel de données. Voila, vous avez une réplication master/slave. La journalisation peut aussi être utilisé pour faire des snapshots, pour conserver une copie des données dans un état cohérent, sans arrêter le service, en recopiant le référentiel et le journal des modifications. On prendra soin de reconsolider les données (en appliquant les modifications sur le référentiel) un peu plus tard, ou sur une autre machine. Ce flot de réplication peut être utilisé de manière plus créative encore, comme changer de format de stockage, un mongodb qui se réplique dans un postgresql, par exemple : mosql ou dans un Elastic Search. Il est possible de reconstruire une série d'événements en comparant deux référentiels de données statiques (enfin, plutôt statiques, on parle plus d'auto-réparation que de synchronisation stricte), c'est ce que propose rsync, mais aussi de manière plus efficace les arbres de Merkell, que les bases "eventualy consistent" apprécient tant. De manière pragmatique, avant d'utiliser des arbres, elles commencent par comparer leurs propres journaux d'événements, et passent à quelque chose de plus systématique si il y a trop de divergences ou que les logs ne vont pas assez loin. Les systèmes de fichiers traditionnels ne peuvent pas utiliser ce genre d'optimisation, inotify est incapable de surveiller une arborescence complexe, énumérer une arborescence est peu efficace (essayez de jouer avec find sur un disque de quelques To).

Traiter le flot

Twitter a démocratisé la notion de fire hose, la lance à incendie en cassant un des principes de bases d'HTTP. En se branchant sur l'API REST de Twitter, un simple GET déclenche une réponse sans fin, les tweets arrivent à la queue leu leu. Il existe aussi une API en XMPP, mais qui se soucie d'XMPP en 2013? Les gens qui font du PubSubHubHub? Elastic Search, en plus de ses Rivers, propose aussi la notion de Percolators dans un Elastic Search pour l'indexer, au fil de l'eau. La percolation propose simplement d'inverser le flot classique, on créer une question, puis on lui soumet des documents et il réponds oui ou non. Analyser son flot d'événements en temps réel est très tentant, mais il faut arriver à suivre le débit et ne pas finir noyé. La réponse officielle est "back pressure" : on remplit une queue que l'on dépile ensuite, à son rythme, avec plus ou moins de workers, en attendant que ça se tasse. RabbitMQ est très fier d'accomplir cette tâche, mais il existe des outils plus simples, comme Redis; ou plus spécialisés comme la Nsq de Bitly. Le dispatcher peut avoir une approche naïve dans son traitement du flot de données, mais il peut aussi assurer un rôle de routage et même de normalisation du contenu des flux. Logstash propose de faire ça derrière un RabbitMQ, Heka, de Mozilla, propose de faire le routage et la normalisation en amont, déléguant la gestion de queue et le traitement en aval (il sait écrire dans un fichier, piper sur une connexion TCP, ou utiliser une base en round robin compatible avec Graphite). Le traitement n'est pas nécessairement unique, un même événement peut déclencher plusieurs actions.

Archiver

Historiquement, syslog (avec ses implémentations modernes rsyslog et syslog-ng) est l'outil pour loguer et envoyer des flots sur une machine distante. Syslog souffre de choix très Unixiens comme le fait de mettre des noms en durs pour des services qui ne sont plus utilisés depuis des lustres, et de ne laisser que 6 emplacements pour des logs métiers. Syslog reste un outil standard et historique qui rends encore beaucoup de services, surtout avec l'aide de Logrotate, il faut juste éviter de l'utiliser pour des besoins métier.

Pour des besoins d'archivages, la tentation est grande d'utiliser des fichiers plats que l'on découpe par date avant des les compresser, pour ensuite les interroger avec zgrep. Classique et de bon goût sur un seul serveur, cette solution devient très pénible sur un ensemble de serveurs, et encore pire si on commence à s'amuser avec une des finalités du cloud : passer son temps à créer puis à jeter des machines. Des solutions plus pérennes comme S3 ou HDFS sont à envisager, pour du stockage brut, mais aussi Mysql pour profiter de sa réplication, comme l'utilise Monolith, de Mozilla.

L'idée de ce genre d'archives d'événements est de bien profiter du rangement par date : on commence par choisir une période de temps avant d'itérer brutalement sur le contenu des fichiers. Si le stockage est distribué, les itérations le seront aussi, et profiteront de la répartitions des temps d'accès des disques durs et des processeurs, pour ensuite essayer de saturer le réseau pour rapporter une réponse.

Il est possible de normaliser faiblement les données (en JSON, par exemple) ou de manière plus agressive, comme les bases statiques de Disco : discodb.

Compter

La machines adorent compter, en fait, c'est peu coûteux, et ça permet d'obtenir des résultats directement utilisables. Statsd et ses clones le font très bien, mais Redis aussi est très fort dans ce domaine. L'idée est de faire des opérations simples sur une fenêtre de temps, court, puis de consolider sur une période plus raisonnable, pour archive. Les opérations peuvent être un simple compteur, mais aussi une moyenne, une médiane, un percentile, un minimum, un maximum, une comparaison avec un autre compteur, bref des statistiques simples. Statsd, qui est très poli, a choisi de ne pas être bloquant, il écoute en UDP, le message pouvant être perdu, mais garanti un niveau d'intrusion minimale. Statsd est conçu pour être utilisé par un développeur, pour qu'il place lui même des compteurs dans son code. Dans le cas d'une analyse de flot, il est plus sage de gérer ce genre de consolidation. Le résultat de ces calculs sont traditionnellement stockés dans des bases en round robin, garantissant une taille constante de données (Rrdtool et Whisper sont deux classiques), et une consolidation garantie par la base. Mais la gourmandise actuelle, et la taille sans cesse croissante des clusters poussent vers un stockage plus brutal, sur des périodes de rétention très longues (quelques dizaines d'années) dans des bases NoSQL comme Hbase ou Riak.

Préparer la recherche

La plus rapide des réponses est une réponse toute faite. C'est le principe de la dénormalisation. Reddit aime beaucoup Cassandra pour ça, avant, ils remplissaient sans compter des Memcaches, puis sont passé à MemcacheDB, le cout de reconstruction d'un Memache vautré étant prohibitif avant de passer à quelque chose de sérieux : Cassandra. Chaque édition d'une nouvelle met à jour une cascade d'index, et l'affichage d'une page est juste un assemblage de données simples. Cette approche, naïve et violente, n'est pas toujours possible, il faut pouvoir invalider simplement un cache et ne pas avoir un nombre infini de combinaisons. Une approche intermédiaire est possible, en conservant un peu de données dénormalisées que l'on peut assembler à la demande. Le stockage promet de belles choses, et ça tombe bien, du code libre commence à apparaitre : Tokudb qui propose un moteur pour Mysql et une base simple de type BerkleyDb.

Index

Les index font partie des outils de bases des bases de données, mais ne sont pas forcément adaptés aux bases distribuées. Riak s'est un peu pris les pieds dans le tapis avec ça, malgré l'usage de LevelDb, et MongoDb a aussi beaucoup de mal avec ses index.

Memcache reste incontournable avec sa politique d'éviction des vieilles clefs, mais Redis permet d'effectuer beaucoup de tâches simples et atomiques, ce qui peut le qualifier de Memcache avec un cerveau. Tous les deux travaillent en RAM, mais Memcache cache, et Redis indexe.

Index inversé

L'index inversé est plus coûteux à maintenir lors des cycles d'écritures, mais plus efficace en lecture. Il est plus connu sous le nom de moteur de recherche. Longtemps cantonné dans des domaines avec peu d'écriture pour beaucoup de lecture, Elastic Search à prouvé qu'en maitrisant le sharding, les threads et la RAM, on peut obtenir des débits en écritures tout à fait correct. En ajoutant une bonne gestion des facettes, on peut basculer en mode "quasi temps réel" d'analyse de flot de données. Les fonctions de calcul de facettes (statistiques et histogrammes) sont les armes secrètes d'Elastic Search.

Bases orientés colonnes

Le principe des bases orienté colonne est simple : on range les données par colonne, et non par ligne. Pour une colonne, on a une liste de valeurs, avec la référence vers la clef de la ligne. Il est souhaitable de trier les valeurs pour pouvoir faire des range query. Les lectures se font de manières séquentielles et peu coûteuses. Cassandra qui fait partie de ce genre de bases, prévu pour gérer des volumes énormes, a des règles simples : la dénormalisation est la norme, "dis moi qu'elle est ta requête, je te dirais ce que tu vas stocker". Cassandra utilise massivement le sharding et la consolidation pour compenser le surcoût du stockage par colonne. Très à l'aise dans les appels simples, ou même les itérations, il aura du mal pour les requêtes non prévus, qui auront besoin d'une préparation en batch, avant de devenir efficace.

Bases orientées graphes

Les bases orientées graphes, considèrent que tout est noeud, relié à d'autres noeuds par une relation. Longtemps contraintes à la quantité de RAM disponible, elles sont maintenant passé à la taille supérieure avec FlockDB qui s'appuie sur Mysql, ou Titan qui s'appuie sur Cassandra ou Hbase. Neo4j, plus modeste est pourtant l'initiateur de nouveaux outils, comme Gremlins qui peuvent profiter de la taille de Titan. Ce genre de base est le Graal du sémantique, et se prête bien aux moteurs de recommandations. Parfaites pour explorer des données (datamining est un terme plus classe), elles sont peu adaptées à traiter des flots de données.

Traitement distribué

Pour des traitements plus complexe que des opérations arithmétiques, il va falloir passer à des outils distribués. Hadoop et ses amis (Hive, Pig...) sont prévus pour du traitement par lot, sur des données fixes. Il existe de jolis applications d'analyse (avec des mots magiques comme OLAP, hypercube ...) : Pentaho par exemple, comme exemple de libre, mais il n'est pas forcément prévus pour gérer des pétas de données. Pour du temps réel, il faut passer à des choses plus spécifiques. Le débit à traiter va rapidement dépasser les capacités d'une machine raisonnable, et dès que l'on commence à jouer avec des grappes de serveurs, le réseau devient vite un goulet d'étranglement, ça tombe bien, vous aviez déjà le disque dur comme ennemi. Pour résumer, votre meilleur ennemis sont maintenant les IO, Input/Output, pour leur débit et leur fiabilité relative. Pour gérer ce genre de chose, ainsi que de multiples connections, il faut une technologie qui gère la parallélisation massive, et donc une boucle événementielle. Scala en a fait sa spécialité : la maturité de la JVM avec la puissance du pattern acteur et de la programmation fonctionnelle. Mais il existe de la concurrence avec Clojure qui joue de mêmes atouts, ou même du vénérable Erlang. Le langage ne fait pas tout, le framework (Kafka, Storm, Spark ...) a un rôle très important : il assure la distribution, la résilience, la répartition des ressources et le moyen de voir ce qui se passe en cas de soucis. Ces outils sont prévus pour taper fort, pour des volumétries que peu atteignent. Des technos plus classiques ont encore leur rôle à jouer pour des échelles moindres, et golang se positionne parfaitement dans ce rôle : on commence par découper son besoin en différents services, et l'on remplace ensuite ceux qui posent problèmes par du go, qui à le bon goût de ressembler à plein de choses, d'avoir une qualité stricte et une efficacité étonnante. Il y a pour l'instant peu d'applications disponibles, Google a son F1 qu'il garde pour lui, Amazon veut bien prêter Redshift, et Elastic Search se lance sur le créneau avec Kibana 3 , une version générique de Kibana, projet lié à Logstash.

Ceinture et bretelles

L'arme ultime n'existe pas, et en plus, elle est souvent trop grosse pour vous. Les drames pourront être comme les datas : big. La composition et le pragmatisme seront plus efficace. Pour du traitement temps réel, on aura souvent un travail en plusieurs étapes : un empilage massif et maladif de données, une analyse à froid, avec du classique map reduce, puis la construction de modèle intermédiaire, couplé avec une partie de données en RAM. Les recherches devenant alors une composition entre les données en RAM et des données consolidées sur le disque dur. La notion de temps réel reste un terme commercial, rien de comparable avec le temps réel dur sur du matériel embarqué. Si on ne se rends pas compte que les données sont anciennes, c'est qu'elles sont temps réel. Ce qui est intéressant dans le big data et son ami le data mining est la découverte d'information à partir d'un gros tas de données. Tout le sport consiste à réconcilier cette exploration et son usage à partir d'un flot de données de manière réactive. Ca donne de jolis schéma dans le genre de la recommandation de Netflix. Ah oui, comme nous sommes dans le monde réel, il faut ajouter à ça : la survie aux crashs, une qualité constante au fil du temps, et un coût d'hébergement inférieur aux bénéfices tirés de l'application. Un peu de temps aussi pour dormir, ce n'est pas mal.


Partager cet article :