Wednesday, June 20, 2012

Orientée colonnes ?

Les bases NoSQL sont arrivées avec leur cortège de nouveautés et pour certaines d'entre elles une notion héritée de BigTable : celle de base de donnée orientée colonne.

Cependant faire le lien entre l'article de Wikipedia et comprendre ce que permet réellement un base de donnée comme HBase n'est pas une chose évidente. En effet le simple fait de définir cette notion ne suffit pas toujours a bien comprendre quels sont les principes de conception du monde SQL qui peuvent être oubliés et ceux qui doivent être appris.

Colonne or not colonne ?

Prenons un modèle très simple de donnée et essayons de le transposer dans un modèle "orienté colonne":
Comme on peut le voir on est passé d'un modèle à 2 dimensions (ligne x colonne) vers un modèle où une valeur est accédée au travers de 2 coordonnées qui sont ici (ligne, colonne) Cette notion de coordonnées est  importante (c'est pour ça que je la met en gras 2 fois de suite) si l'on veut concevoir une gestion des données avec une base orientées colonnes. Plus vite vous intégrerez le fait qu'une colonne n'est qu'un élément d'une coordonnée et que vous pouvez y mettre ce que bon vous semble (même de la donnée !) mieux vous vous porterez. Notez que la clé primaire dans la colonne Id est devenue ce que l'on appelle une row dans l'API de HBase. Cette appellation dans l'API est un peu déroutante on parlera plus souvent de row key.

Avantages :
  • L'ajout de données se fait sur une seule dimension ce qui est techniquement plus simple et plus rapide : les données sont concaténées les unes à la suite des autres, HBase supportent ainsi des débits en écritures bien plus élevés que ses consœurs avec des temps de latence très réduits.
  • On devient à la mode en étant "scalable" (utilisez votre meilleur accent anglais pour faire bien) :  comme le développement des données ne se fait que sur une seule dimension leurs partitionnements est plus simple à réaliser et on peut les distribuer sur plusieurs serveurs. (dans le cas de HBase on va appeler ça des Regions)
  • Ajouter une colonne devient trivial car il s'agit au final seulement d'ajouter un nouveau tuple, on peut même se permettre le luxe (ou l'horreur, c'est selon) de n'ajouter qu'une seule colonne à une ligne en particulier : là où certaines bases de données vont réserver de la place pour toutes les autres lignes cela ne coutera rien d'autre que la valeur et ses coordonnées avec HBase.
Inconvénient :
  • La notion de "coordonnées" suppose que ces dernières permettent de retrouver de manière univoque la donnée, comme on peut le voir dans l'exemple la colonne appelée Id est une clé primaire, il s'agit d'une pratique fréquente dans la conception des schéma de base. C'est aussi le cas dans une base orientée colonne mais ici elle revêt un rôle de tout premier ordre puisque que la clé primaire est obligatoire et qu'elle sera le moyen le plus efficace de retrouver la donnée. Autrement dit : il y a une seul clé, seul champ véritablement indexé, les recherches sur les autres colonnes seront moins efficaces. (attention aux bases NoSQL qui vous promettent des indexes secondaires, lisez bien les petites lignes du contrat)
  • Si ce type de base de données est très efficace en écriture elle l'est moins en lecture, non seulement on se retrouve avec une seule clé mais on voit aussi assez vite que si l'on veut être performant et pouvoir distribuer les données il va falloir mettre un peu d'ordre en les triant et user d'outil tels que les filtres de M. Bloom afin d'optimiser les recherches.
  • Une mise à jour n'écrase pas directement l'ancienne valeur, il faut donc prévoir à un moment ou un autre un processus appelé "compaction" pour réellement supprimer les données. Noter que les suppressions de données se font exactement comme les mises à jour, on place juste ce que l'on appelle une pierre tombale ("tombstone") à la place de la donnée proprement dite.

Comment est-ce qu'HBase implémente ce principe ?

En réalité HBase ne va pas se compliquer la vie avec les notions de "ligne" et de "colonne" telles qu'on peut l'entendre dans le monde relationnel. Si vous avez bien compris le point précédent une donnée devient accessible grâce à des coordonnées. HBase stocke systématiquement une donnée avec l'ensemble de ses coordonnées. Avec ce modèle HBase n'est plus limité à un espace à 2 dimensions et elle en profite pour ajouter une notion de famille de colonne et un horodatage qui va permettre de gérer plusieurs versions d'une donnée :
La position d'une donnée dans HBase peut donc être décrite par les coordonnées :
(row, column family, column qualifier, timestamp)

L'objet qui stocke à la fois les coordonnées et la donnée est org.apache.hadoop.hbase.KeyValue. (cependant comme vous pourrez le voir l'ensemble est sérialisé dans un tableau de byte ce n'est donc pas très palpitant à regarder). Les milliards de KeyValue qui représentent vos données sont stockés dans un type de fichier propre à HBase appelé HFile (dont la version 2 est une contribution de Facebook). Ces fichiers sont eux même stockées par le système de fichier sous-jacent (en règle générale il s'agit de HDFS qui assure la réplication des données).

Est-ce que cela veut dire qu'une partie des coordonnées est dupliquée pour chaque nouvelle donnée insérée ? Oui, il s'agit là d'un des défaut de cette implémentation, certains éléments comme la ligne ou le nom de la famille de colonne sont dupliquées. Si la clé de la ligne fait 20 bits (c'est un exemple) et qu'elle possède 11 "colonnes" alors elle sera dupliquée d'autant (dans cet exemple on peut grossièrement quantifier cette "surcharge" à 200 bits). Moralité : essayez de contenir la longueur des lignes et des noms de famille de colonne. Vous pouvez activer le Data Block Encoding arrivé avec HBase 0.94 qui pallie ce "problème" et activer une compression comme Snappy ou LZO.

Autre remarque très importante : les KeyValue sont toujours stockées dans l'ordre au sein d'un HFile, il est donc très performant de lire plusieurs KeyValue contiguës. Le format HFile dispose de nombreux artifices (regroupement en bloc, indexation, filtre de Bloom, compression...) afin d'optimiser les accès en lecture mais comme ce n'est pas le sujet de ce post je ne m'étendrai pas dessus.

Comment exploiter ce principe lors de la conception d'un schéma ?

Un exemple valant mieux qu'un long discours je vais prendre celui de l’excellent OpenTSDB.

Qu'est ce qu'OpenTSDB ?

OpenTSDB est un logiciel qui permet de tracer des métriques au fil du temps :
  • Le type de métrique en lui même importe peu si l'on est capable de lui attribuer une valeur numérique (e.g. : la mémoire disponible sur une machine sera exprimée sous la forme d'un entier) et un nom (e.g. free.memory)
  • Vous pouvez associer une ou plusieurs étiquettes (tag) à un métrique (e.g. le nom d’hôte, sa localisation géographique...).
  • La précision peut descendre à la seconde.
 L'originalité d'OpenTSDB est d'utiliser HBase pour conserver les métriques collectés, le volume de données qui peut être ingurgité par le système est donc très important, pour paraphraser le site officiel : "collect many thousands of metrics from thousands of hosts and applications".

Le schema de OpenTSDB

  1. Ligne (row) : Elles sont la concaténation du métrique, de la date arrondie à l'heure et du (des) tag(s). Dit autrement une ligne va contenir toutes les données collectées dans un intervalle d'une heure pour un métrique et un ou plusieurs tags donnés. Notez que la date n'est placée qu'en deuxième position afin d'éviter qu'à un instance t toutes les requêtes en écritures arrivent sur la même Region
  2. La nom de la famille de colonne (column family) : il est fixé à 't' , plus le nom de la famille de colonne est petit mieux c'est.
  3. Les noms de colonnes  (qualifiers)  : le nom de chaque colonne est le décalage en secondes entre la date arrondie et la date effective de capture du métrique
Sur un exemple simple voici de manière un peu simplifié comment OpenTSDB stocke les données dans HBase :


L'originalité de ce schéma est d'utiliser le nom de la colonne pour y stocker de l'information, il s'agit là d'une manière très élégante d'utiliser les capacités d'une base de données orientée colonnes. Encore une fois il est intéressant de raisonner en terme de coordonnées plutôt qu'en terme de colonnes.
Vous trouverez une description plus complète et précise du schéma ici : http://opentsdb.net/schema.html

Par où continuer ?

Je n'ai volontairement pas abordé certains points comme celui des familles de colonnes, de la distribution des lignes sur les nœuds qui compose un cluster HBase, etc.... Si vous souhaitez aller plus loin avec Hbase il y a le livre "HBase : The Definitive Guide" de Lars George. Lars a aussi dirigé une conférence web dédiée à cette problématique, elle est disponible sur Youtube : HBase Schema Design - Things you need to know
Enfin en ligne le HBase reference Guide est aussi un bon point de départ.

A noter SalesForce qui a réalisé une très bonne présentation sur le sujet de la conception de schéma lors de la HBaseCon 2012 : la vidéo est ici et la présentation est là : 

No comments:

Post a Comment