Le livre "Practical Cassandra: A Developer's Approach", de Russell Bradberry and Eric Lubow, est un guide de développement expliquant comment construire des applications s’appuyant sur la base de données NoSQL Cassandra.
Ce livre couvre tous les aspects du cycle de développement Cassandra, tels que l’installation, la modélisation des données, le modèle de requête (CQL), le tuning des performances et le monitoring. Les auteurs discutent aussi des différents drivers permettant d’accéder aux tables de la base de données et présentent des exemples de code pour les langages classiques comme Java, C#, Ruby et Python. Le livre propose également plusieurs études de cas présentant l’utilisation de Cassandra dans le cadre de différentes applications, parmi lesquelles se trouvent des applications utilisées chez Ooyala, Hailo et eBay. InfoQ s’est entretenu avec les auteurs pour discuter du modèle de données de Cassandra, des problématiques de conception et de la façon qu’a Cassandra de gérer la concurrence et le versioning des jeux de données.
InfoQ : Pouvez-vous nous dire ce qu’est Cassandra ? Une chose porte souvent à confusion : s’agit-il d’une base orientée-colonne de type "Columnar" ou d’une base orientée-ligne de type "Familles de colonnes" (Row based Column Family) ?
Bradberry & Lubow : Cassandra est une base orientée-ligne de type "Familles de colonnes". Ceci signifie que la donnée est stockée sous forme de "lignes" plutôt que de "colonnes" et de façon à ce que toutes les lignes correspondant à une même "partition" définie par l’utilisateur soient stockées ensemble, de façon ordonnée, sur les disques. Cela garantit que toutes les lignes d’une partition donnée se trouvent sur un même nœud et fait que le parcours séquentiel des données est non seulement possible mais aussi très performant. Le terme "Column Family" vient du livre blanc sur BigTable de Google, qui a influencé le modèle de stockage de Cassandra.
InfoQ : Quelles sont les problématiques de conception à considérer lorsqu’il s’agit de travailler avec une base de type "Column Family" comme Cassandra ?
Bradberry & Lubow : Vous devez modéliser vos requêtes, pas vos données. Toutes les tables Cassandra ont une clé primaire. Cette clé est composée de 2 parties distinctes : les "colonnes de partition" et les "colonnes de clustering". Les colonnes de partition déterminent sur quels nœuds vos données vont vivre, les colonnes de clustering déterminent l’ordre des données au sein d’une partition. Lorsque vous requêtez vos données, vous devez indiquer au grand minimum les colonnes de partition. Ceci implique que vous devez avoir une certaine connaissance de vos données avant de les requêter ; il n’y a pas de recherche ad-hoc dans Cassandra. Dans le cas où la récupération de vos données doit satisfaire deux requêtes différentes, alors vous devez créer une autre table, qui stocke les données de telle sorte que celles-ci puissent être récupérées selon ce mode de requête.
La duplication des données n’est pas un problème. Cassandra a été construite selon le postulat que "l’espace disque ne coûte pas cher". En partant de ce principe, la duplication ne doit pas être vue comme une mauvaise chose. En tant qu’administrateurs de bases de données, on nous a toujours appris qu’il ne fallait pas dupliquer la donnée. Ainsi, nous devions "normaliser" la donnée au sein de différentes tables qu’il fallait ensuite joindre et réassembler pour fournir la vue sur les données qui nous était demandée. Bien que cela économise de l’espace disque, c’est très inefficace sur de nombreux modèles de données, en particulier lorsque l’on considère que le coût du stockage au gigabyte peut descendre au-dessous de 4 cents.
InfoQ : Dans le livre, il y a un chapitre sur la modélisation des données. Quel est le processus classique de modélisation avec une base de type "Column Family" et quelles sont les différences avec le processus appliqué aux RDBMS ?
Bradberry & Lubow : Comme cela a été dit dans la réponse précédente, la modélisation des données avec un RDBMS s’attache, entre autres, à chercher l’efficacité du stockage. Cela remonte à l’époque où le stockage n’était pas bon marché. Les données dans Cassandra sont également modélisées pour l’efficacité, mais pour l’efficacité au moment de leur récupération, pas pour minimiser le nombre d’octets sur les disques.
InfoQ : Pourriez-vous nous dire quelques mots au sujet des modèles de clustering et de failover de Cassandra ? Quelles sont les différences entre les modèles "peer-to-peer" et "maître/esclave" tels que supportés par d’autres bases de données et quels sont les avantages et les inconvénients de chacun ?
Bradberry & Lubow : Cassandra n’utilise pas de modèle de failover en soi. Quand on parle de failover, cela a plutôt rapport avec les relations maître/esclave où le primaire doit alerter les applications que la source d’autorité sur les lectures/écritures (le maître) a changé d’endroit. Puisque Cassandra ne fonctionne pas selon des règles de maître/esclave, le failover (au sens traditionnel) n’est pas requis. Quand un driver se connecte à Cassandra, il reçoit une liste de tous les nœuds disponibles dans le cluster et peut écrire et lire depuis n’importe lequel de ces nœuds. La responsabilité de localiser la donnée (si elle existe) est confiée au nœud auquel on s’adresse, tandis que le driver a la responsabilité de savoir si le nœud auquel il essaie de se connecter est disponible ou pas. L’avantage de ce modèle est qu’il permet une disponibilité beaucoup plus élevée. Un système à haute disponibilité implique moins d’interruptions de service, ce qui profite aux applications et aux utilisateurs. L’inconvénient de ce modèle est que le driver doit être au courant de l’état de tous les nœuds du cluster avec lesquels il est susceptible de se connecter. Ceci peut ajouter de la complexité aux drivers, ainsi qu’un peu de charge sur les serveurs d’applications.
Le modèle maître/esclave a aussi des avantages. Avec ce modèle, il est beaucoup plus facile d’augmenter de façon horizontale la capacité de lecture : il suffit d’ajouter quelques esclaves pour que cette capacité augmente. Quand on ajoute des nœuds, la capacité d’écriture augmente aussi, mais dans ce cas, une approche similaire existe avec Cassandra. Avec les systèmes maître/esclave, on a plus fréquemment tendance à augmenter la capacité de façon verticale : vous pouvez utiliser des machines plus costaudes pour le master (ainsi que pour son serveur en hot standby). Pour le scaling d’un cluster Cassandra, pour que les choses se passent bien, il faut généralement remplacer tous les nœuds du datacenter (pas nécessairement tout l’anneau).
Un des principaux avantages (ou inconvénients, selon votre point de vue) de l’approche appliquée par Cassandra est que la donnée est éventuellement consistante. Il existe des façons d’assurer un niveau de consistance plus élevé, comme faire des lectures ou écritures en exigeant qu’un certain nombre de nœuds assurent le fait que l’écriture est bien effective ou que la donnée lue est bien en cohérence avec les dernières mises à jour de l’objet en question.
InfoQ : Comment sont gérés le versioning et la concurrence pour permettre à Cassandra de proposer un accès aux données scalable ?
Bradberry & Lubow : En ce qui concerne le versioning des données, Cassandra opte pour une approche selon laquelle "la dernière écriture gagne" (last-write-wins ou LWW). Ce choix a été fait pour permettre un modèle plus simple que celui de l’horloge vectorielle (vector clock) standard, où la décision de la donnée correcte à stocker en base est remise au client. L’idée du LWW fonctionne pour Cassandra car le modèle de données est fait de telle sorte que les lignes sont découpées en petites parties, les colonnes, et que la résolution des timestamps se fait pour chacune de ces colonnes. Cette approche s’applique jusqu’au versioning du schéma car le schéma est lui aussi stocké dans Cassandra. Ainsi, lorsque plusieurs mises-à-jour de schéma sont faites, celles-ci sont appliquées dans l’ordre où elles ont été reçues. On peut dire la même chose de la gestion de la concurrence. Lorsque plusieurs mutations sont envoyées à Cassandra au même moment (ou de façon rapprochée) et que celles-ci modifient le même objet, alors la stratégie LWW sera appliquée à chaque colonne, de façon individuelle, afin de décider quelle mutation "gagne".
Un autre concept important pour le support de la concurrence est la capacité de choisir le niveau de consistance (Consistency Level, CL) à appliquer aux écritures et lectures de données. En accordant un niveau d’exactitude plus élevé à certains types de données de votre application (écrire et lire à un CL plus haut), vous pouvez permettre à votre système d’exécuter plus d’opérations concurrentes tout en exécutant moins d’opérations coûteuses sur le cluster.
InfoQ : Quelles sont les limitations des bases "Column Family" par rapport aux bases relationnelles et aux autres bases NoSQL ?
Bradberry & Lubow : Le fait qu’il n’y ait pas de requêtage ad-hoc. Si vous souhaitez requêter vos données, vous devez les stocker de sorte que cela soit facile de les récupérer. Il n’y a pas de fonctions d’agrégation et les possibilités d’ordonner sont très limitées. Les quelques méthodes existantes en ce domaine (comme les index secondaires ou la méthode Count en CQL) ont tendance à devenir inutilisables rapidement, le volume de données augmentant.
InfoQ : Vous discutez aussi de techniques de mise-au-point des performances dans le livre. Pourriez-vous nous parler de quelques-unes de ces techniques et nous expliquer comment celles-ci aident pour la scalabilité et la performance des applications ?
Bradberry & Lubow : Les méthodes abordées dans le livre sont en gros de 3 types : le tuning de la JVM, l’utilisation des options de Cassandra et le tuning au niveau système (ou kernel). Sans aller dans les détails, puisque ceux-ci sont donnés dans le livre, il est utile de voir quel est le raisonnement derrière chacune de ces 3 catégories.
En commençant au plus bas niveau, le kernel contient les jeux d’instructions que le système d’exploitation utilise pour décider comment il va gérer sa charge de travail. Ici, on peut donc configurer les priorités des opérations disque, réseau, CPU, etc. Dans le cas où nous sollicitons intensivement le réseau, nous voudrons donner au kernel les instructions correspondantes pour supporter ce cas d’utilisation. En d’autres termes, on voudra configurer le système en donnant une priorité plus haute au trafic réseau car c’est comme cela que l’application utilise la base de données. Ou alors, si notre application exécute constamment des requêtes complexes, on voudra donner plus de priorité aux IO.
La seconde catégorie en remontant la stack est la JVM. C’est le conteneur d’exécution pour Cassandra. Il existe un très grand nombre de paramètres pour la JVM, de la mémoire à la garbage collection. En fonction de votre utilisation de Cassandra, vous pouvez grandement impacter la performance en changeant ces paramètres. Vous pourrez généralement trouver des ajustements intelligents sur la garbage collection une fois que vous connaîtrez mieux votre mode d’utilisation. Vous pourrez aussi ajuster l’utilisation de la mémoire sur l’ensemble de la JVM. Le point important qui ressort de tout cela, c’est qu’il ne faut utiliser ces paramètres qu’une fois que l’on connaît vraiment son mode d’utilisation et la façon dont ces paramètres affectent le système.
La troisième catégorie, tout en haut de la stack, correspond à Cassandra. Il y a beaucoup de clés de paramétrage dans Cassandra concernant la façon dont sont stockées les données et comment on y accède (comme les paramètres de compression ou du bloom filter). Ces paramètres peuvent avoir un impact sur la façon dont les nœuds stockent les données sur disque (compression), comment les nœuds communiquent entre eux pour déterminer l’état de santé (gossip, détection de panne, chiffrement) et comment ceux-ci allouent et nettoient la mémoire au sein de la JVM. Tous ces paramètres peuvent avoir un fort impact sur le système. Mal paramétrer l’utilisation de la mémoire peut mettre un serveur Cassandra sur les genoux et peut même affecter l’ensemble du cluster.
Le tuning se ramène à la capacité à connaître son système, à la façon dont son application fonctionne et ce pour quoi elle devrait être optimisée. Une fois que les objectifs de performance pour votre application sont clairs, savoir ce qui doit être réglé aux différents niveaux de l’infrastructure devient beaucoup plus évident.
InfoQ : Quelles sont les fonctionnalités intéressantes apportées par CQL3 ?
Bradberry & Lubow : CQL3 fournit une façon simple et familière de requêter Cassandra. Celui-ci propose un protocole binaire pour communiquer de façon efficace avec Cassandra. Le protocole permet aux clients d’être notifiés lorsqu’un nœud entre ou sort de l’anneau ainsi que de découvrir l’anneau entier sans avoir la connaissance du nom de tous les hôtes. Il permet aussi de créer des instructions CQL préparées, ce qui diminue le temps passé à parser les requêtes CQL. En plus de ce protocole binaire, CQL3 introduit la notion de Collections, qui offre au client la possibilité de stocker des types Set, List et Map en plus des types de base proposés par Cassandra.
InfoQ : De quels outils disposent les développeurs pour utiliser Cassandra ?
Bradberry & Lubow : Les docs DataStax apportent beaucoup d’informations, de même que OpsCenter et DevCenter de DataStax. Vous pouvez compter également sur les forums utilisateurs, une mailing list et un bon nombre de personnes qui suivent Cassandra sur StackOverflow. Si vous avez besoin de réponses plus rapides, vous trouverez beaucoup de gens qui utilisent Cassandra sur #cassandra de Freenode IRC.
InfoQ : Y a-t-il des fonctionnalités que vous aimeriez voir ajouter à Cassandra dans les prochaines releases ?
Bradberry & Lubow : Nous adorerions avoir des fonctionnalités de streaming de données, par exemple être capable d’exécuter de grosses requêtes et de streamer les données alors que le client les lit. Des compteurs idempotents seraient très intéressants aussi, ainsi que la capacité de voir le statut d’un ensemble de tâches de réparation, les repairs.
Russel et Eric nous ont aussi parlé de la façon d’aborder les solutions NoSQL dans le cadre de différents cas d’utilisation :
Bradberry & Lubow : Il est important de comprendre que toutes les solutions NoSQL ne sont pas égales. Les cas d’utilisation où Cassandra est appropriée ne sont pas les mêmes que ceux où MongoDB est approprié. On ne devrait se demander si l’on doit utiliser X ou Y qu’après avoir examiné profondément son cas d’utilisation et considéré en quoi il correspond à un système donné de base de données. 99% des cas peuvent se satisfaire d’un simple PostgreSQL.
Au sujet des Auteurs
Russell Bradberry est l’auteur principal du driver NodeJS pour Cassandra, Helenus. En tant qu’architecte chez SimpleReach, Russell est responsable de l’architecture et de la mise-en-œuvre de solutions orientées données hautement scalables. Il a participé à la création de nombreux produits, dont un serveur d’enchères publicitaires temps-réel, un outil de gestion de médias riches publicitaires, un système de recommandation et plus récemment, une plateforme de Social Intelligence temps-réel. Il a reçu un MVP de DataStax pour Apache Cassandra.
Eric Lubow, le CTO de SimpleReach, construit des systèmes distribués hautement scalables pour le traitement de données sociales. Il a commencé sa carrière dans la mise-en-œuvre de systèmes Linux sécurisés. Depuis, il a travaillé sur la mise-en-œuvre et l’administration de différents types de systèmes publicitaires, la maintenance et le déploiement d’applications web à grande échelle ainsi que sur le développement de systèmes de distribution et d’analyse d’emails. Il est aussi MVP DataStax pour Apache Cassandra.