Les bases NoSQL en mémoire, telles que les solutions open source Redis et Memcached, sont en train de devenir le standard de-facto pour les applications web et mobile se souciant de l'expérience utilisateur. Pourtant, les grandes entreprises ont résisté à l'adoption de ces bases de données à cause des challenges que cela représente sur le plan des performances, de la scalabilité et de la disponibilité.
Heureusement, on trouve pour les langages (Ruby, Node.js, Python, etc.) et les plate-formes modernes (Rails, Sinatra, Django, etc.) un ensemble d'outils et de librairies qui permettent d'agir sur la performance ainsi que diverses commandes pour les bases en mémoire (en particulier dans le cas de Redis) pour implémenter tout un ensemble de cas d'utilisation parmi les plus courants.
Ces cas d'utilisation peuvent s'appliquer à des projets comme des forums, des analyses temps réel, des clones de Twitter, des recherches géographiques et du cache avancé. Pour chacune de ces applications (et pour tous les cas d'utilisation qui n'ont pas encore été imaginés), la disponibilité, la scalabilité et la performance de la base de données est essentielle pour rencontrer le succès.
Cet article décrit les lignes directrices pour rendre une base NoSQL en mémoire prête pour l'entreprise et donne des conseils et des recommandations sur les façons de surmonter les sept challenges les plus importants liés à la gestion de ces bases sur le Cloud.
1. Disponibilité
Quelque soit votre activité, vos données devraient toujours être disponibles pour votre application. Ceci est extrêmement important dans le cas des bases en mémoire car, sans les mécanismes appropriés en place, vous allez perdre votre jeu de données si un des évènements suivants se produit :
- défaillance d'un noeud (ce qui arrive souvent sur le Cloud)
- redémarrage d'un processus (ce qui peut être requis de temps en temps)
- évènement lié au scaling (ce qu'on peut voir arriver, espérons-le)
Il existe principalement deux mécanismes qui peuvent être implémentés pour supporter les deux premiers cas (le troisième sera abordé plus bas dans cet article) :
- Réplication : Vous devriez vous assurer d'avoir au moins une copie de votre jeu de données hébergée sur une instance distincte, de préférence sur un data center différent si vous souhaitez vous protéger des défaillances au niveau d'un data center, comme cela a pu arriver au moins à quatre reprises sur Amazon Web Services (AWS) au cours de 2012. Est-ce que cela est simple ? Malheureusement, non. Il y a un scénario qui fait de la réplication un challenge :
- Lorsque le débit d'écritures de votre application augmente, certains de vos serveurs peuvent se mettre à écrire plus rapidement que leur vitesse de réplication, surtout s'il y a de la congestion réseau entre votre noeud primaire et les replicas. Et lorsque cela commence à arriver, si votre jeu de données est de taille importante, il y a de fortes chances pour que vos replicas ne soient jamais plus synchronisés.
- Failover automatique : Pourquoi en a-t-on besoin ? Eh bien, votre base en mémoire est généralement capable de traiter 100 fois plus de requêtes/seconde que vos autres bases de données. Donc, chaque seconde supplémentaire d'indisponibilité implique le retard de plus de traitements pour votre application et une mauvaise expérience pour vos utilisateurs. Assurez-vous de bien suivre cette liste lorsque vous mettez en place votre mécanisme de failover automatique :
- La bascule vers le replica doit être faite instantanément lorsque le noeud primaire tombe. Il devrait s'appuyer sur un mécanisme de watchdog qui assure un monitoring constant de vos noeuds et bascule sur le replica le plus apte lors d'une défaillance.
- Le process devrait être le plus transparent possible pour votre application. Idéalement, il ne devrait pas y avoir besoin de changer la configuration. Les solutions les plus avancées se contentent de changer l'IP derrière l'adresse DNS de votre base de données, ce qui assure que le processus de recovery ne prend que quelques secondes.
- Votre failover automatique devrait s'appuyer sur des quorums et devrait être soit complètement consistant, soit éventuellement consistant. Ce sujet est abordé plus bas.
2. Consistance pendant et après les partitionnements réseau
Le Cloud est le Saint Graal des partitionnements réseau, probablement le problème le plus compliqué à traiter pour toutes les bases de données distribuées. Dès lors que cela se produit, votre application peut ne voir qu'une partie de vos noeuds NoSQL, si ce n'est aucun, et chacun de vos noeuds NoSQL peut ne voir qu'une partie des autres noeuds NoSQL.
Pourquoi est-ce un aussi gros problème ? Et bien s'il se trouve que votre base présente un défaut quelconque de conception, votre application peut se retrouver à écrire sur le mauvais noeud pendant que le partitionnement se produit. Ceci signifie que toutes les requêtes d'écriture qu'effectue votre application peuvent être perdues une fois la situation rétablie. C'est une problématique significative avec les bases en mémoire, car le nombre d'écritures par seconde est souvent beaucoup plus élevé qu'avec les autres types de bases NoSQL.
Et que se passe-t-il si votre base ne présente pas de tels défauts de conception ? Pas de magie, vous aurez de toutes façons à choisir entre les deux très mauvaises options suivantes :
- Si votre base en mémoire est considérée comme totalement consistante, vous devriez être conscients que dans certaines situations, celle-ci ne va pas vous permettre d'écrire tant que le partitionnement n'est pas rétabli.
- Si votre base en mémoire est considérée comme éventuellement consistante, votre application utilise probablement un mécanisme de quorum sur les requêtes de lecture, lesquelles vont soit retourner une valeur (sur le quorum), soit être bloquées (en attente d'un quorum).
Note : étant donné qu'il n'existe pas de base en mémoire éventuellement consistante sur le marché actuellement, seule la première option est valable dans un scénario réel.
3. Durabilité des données
Même si votre solution NoSQL en mémoire propose plusieurs options de réplication, vous devriez toujours considérer les options de persistance et de backups pour plusieurs raisons :
- Peut-être ne souhaitez-vous pas payer le surcoût d'une réplication en mémoire mais voulez être sûrs que votre jeu de données soit disponible quelque part et qu'il peut être récupéré (même avec un délai) en cas de défaillance.
- Peut-être que vous voulez être capables de récupérer en cas de défaillance quelconque (au niveau d'un noeud, de plusieurs noeuds, du datacenter, etc.) et que vous voulez garder l'option de maintenir une copie disponible de votre jeu de données quelque part, dans un endroit sûr, même si ce jeu de données n'est pas à jour par rapport aux toutes dernières modifications.
- Pour toute autre raison qui nécessite de la persistance comme des imports des jeux de données de production vers des environnements de staging pour des besoins de debugging.
À présent que vous êtes convaincus, espérons-le, qu'il est nécessaire d'avoir recours à la persistance, vous devriez utiliser dans la plupart des environnements Cloud un matériel de stockage attaché à votre instance Cloud (comme EBS pour AWS, Cloud Drive d'Azure, etc.). Si vous utilisez un disque local, les données seront perdues à la prochaine défaillance.
Une fois la persistance en place, le premier challenge sera de maintenir la vitesse de votre base NoSQL en mémoire tout en écrivant simultanément les changements dans le stockage persistant.
4. Stabilité des performances
Les bases NoSQL en mémoire comme Redis et Memcached sont conçues pour traiter plus de 100k requêtes/seconde avec une latence inférieure à la milliseconde. Mais vous n'atteindrez pas ces chiffres dans votre environnement Cloud à moins que vous ne suiviez les principes suivants :
- Assurez-vous que la solution que vous choisissez utilise les instances Cloud les plus puissantes (comme les instances m2.2xlarge/m2.4xlarge d'AWS ou les instances A6/A7 d'Azure) et un environnement dédié. Sinon, un mécanisme qui protège du phénomène "voisins encombrants" entre les différents comptes Cloud peut être mis en place. Un tel mécanisme doit surveiller la performance de votre jeu de données en temps-réel, à la commande ou de façon régulière, et appliquer des mécanismes tels que la migration du jeu de données vers un autre noeud si la latence dépasse un certain seuil.
- Pour éviter les goulots d'étranglement au niveau des I/O de stockage, assurez-vous que votre solution utilise du matériel de stockage puissant, de préférence configuré en RAID. Ensuite, vérifiez que votre solution ne va pas bloquer votre application lors des situations de forte sollicitation. Par exemple, avec Redis, vous pouvez configurer votre noeud esclave de façon à ce qu'il se charge des écritures persistantes afin de laisser le master traiter les requêtes applicatives et ainsi éviter les timeouts.
- Testez les suggestions des fournisseurs de Cloud en matière d'optimisation des I/O de stockage, comme PIOPS d'AWS. Dans la plupart des cas, ce sont de bonnes solutions pour les accès aléatoires (read/write), mais ne présentent pas de grand avantage par rapport aux configurations de stockage standard pour les scénarios d'écriture séquentielle que l'on retrouve avec les bases en mémoire.
- Si votre base en mémoire est basée sur une architecture single thread, comme l'est Redis, assurez-vous que vous n'exécutez pas plusieurs bases sur le même processus single thread. Cette configuration peut provoquer des situations de blocage, où une base empêche l'exécution de commandes sur les autres bases.
5. Vitesse réseau
La plupart des instances Cloud sont configurées avec une carte NIC 1Gbps unique. Dans le contexte d'une base NoSQL en mémoire, cette interface est sollicitée par :
- les requêtes des applications
- la communication inter-cluster
- la réplication
- les accès au stockage
Celle-ci peut facilement devenir le goulot d'étranglement, voici donc quelques suggestions pour résoudre ce problème :
- Utilisez des instances Cloud avec des interfaces 10Gbps (cependant, soyez préparés, celles-ci sont très chères).
- Choisissez un Cloud, comme AWS, qui offre plus d'une NIC 1Gbps pour certaines configurations spéciales, comme celles en VPC.
- Mettez à profit une solution qui alloue efficacement les ressources entre les noeuds de la base NoSQL en mémoire, afin de minimiser les congestions réseau.
6. Scalabilité
Pour une solution clef/valeur simple comme Memcached ou les formes simples de Redis, le scaling n'est pas considéré comme un gros problème, puisque, dans la plupart des cas, il suffit d'ajouter/supprimer un serveur à/d'une liste et de changer la fonction de hashing. Cependant, les utilisateurs ayant quelques expériences à ce sujet réalisent que le scaling peut être une tâche difficile. Voici quelques recommendations :
- Utilisez un hashing consistant. Si vous faites du scaling avec une fonction de hash simple comme modulo, vous perdrez vos clefs lors des évènements de scaling. D'un autre côté, la plupart des gens ne réalisent pas qu'avec un hashing consistant, vous perdez aussi une partie de vos données au moment du scaling. Par exemple, lors d'un scaling horizontal, vous perdez 1/N de vos clefs, où N est le nombre de noeuds que vous avez après l'évènement de scaling. Donc, si N est petit, cela peut quand même être un processus pénalisant (par exemple, le scaling horizontal d'un cluster de 2 noeuds avec hashing consistant implique la perte d'1/3 de clefs de votre jeu de données).
- Construisez un mécanisme qui synchronise tous les clients de votre base NoSQL en mémoire avec l'évènement de scaling, afin d'éviter une situation où différents serveurs applicatifs écrivent sur différents noeuds pendant l'évènement.
Le scaling devient un vrai problème lorsque l'on doit composer avec des commandes complexes comme UNION ou INTERSECT de Redis. Ces commandes sont l'équivalent du JOIN en SQL et ne peuvent pas être implémentées au-dessus d'une architecture multi-shard sans ajouter un délai et une complexité significatifs. Le sharding au niveau applicatif peut résoudre certaines problématiques en permettant d'exécuter des commandes complexes au niveau des shards. Cependant, cela requiert une conception complexe, fortement liée à la configuration de vos noeuds NoSQL en mémoire, c'est-à-dire que l'application shardée doit avoir connaissance du noeud sur lequel est stockée chaque clef. De plus, des évènements de scaling comme le re-sharding impliquent des modifications de code et une surcharge opérationnelle.
Sinon, certaines personnes clament que les nouvelles instances à ultra-haute RAM, comme le High Memory Cluster Eight Extra Large 244 GB d'AWS (cr1.8xlarge), résolvent la plupart des problèmes de scaling des types de données complexes grâce à du scaling vertical. La réalité est un peu différente car avec un jeu de données de 25GB-30GB, il y a beaucoup d'autres problématiques à gérer avec une base en mémoire comme Redis, qui empêchent l'exécution du scaling vertical. Ces problématiques sont liées aux challenges décrits plus haut dans cet article, comme la réplication, les I/O de stockage, les architectures single thread limitées à un seul coeur, la surcharge de la bande passante réseau, etc.
7. La surcharge d'exploitation
La prise en charge de tous les aspects opérationnels d'une base NoSQL en mémoire ajoute une charge significative. Cela requiert une compréhension profonde de tous les détails de toutes ces technologies afin de pouvoir prendre les bonnes décisions aux moments les plus critiques. Cela requiert aussi de rester constamment à jour des dernières nouveautés et tendances de ces systèmes, puisque les technologies changent très souvent (peut-être trop souvent).
Conclusion
Comme mentionné plus haut, il est essentiel d'avoir une bonne compréhension des problématiques rencontrées avec Redis et Memcached afin de tirer complètement avantage de ces technologies open source. Il est spécialement important pour les équipes IT des entreprises de savoir comment maîtriser au mieux ces challenges afin de mettre à profit les bases NoSQL en mémoire en environnement d'entreprise. J'admets que mon avis peut être biaisé, mais je vois énormément de valeur dans les solutions commerciales qui offrent une bonne maîtrise des choses comme la scalabilité et la haute disponibilité sans compromettre les fonctionnalités et la performance, puisque exécuter ces opérations à demeure demande une grande expertise dans le domaine et que ce savoir-faire est rare.
Il existe quelques solutions NoSQL-as-a-service en mémoire sur le marché pour Redis et Memcached. Je recommande de conduire une comparaison complète entre tous les services disponibles, en incluant une approche DIY, afin de déterminer la meilleure façon pour votre application de maîtriser les challenges de la gestion des bases en mémoire sur le Cloud. Une bonne idée est d'expérimenter en grandeur nature avec les candidats les mieux placés pour vous ; certains services proposent des essais gratuits exactement dans cette perspective.
Au sujet de l'auteur
Yiftach Schoolman est le co-fondateur et le CTO de Garantia Data. Yiftach est un Ingénieur expérimenté ayant joué différents rôles clés dans la conception et le développement de produits dans différents domaines, dont l'accélération logicielle, le Cloud Computing, le Software-as-a-Service (SaaS), les réseaux Broadband et Metro. Yiftach a été le Fondateur, Président et CTO de Crescendo Networks (acquis par F5, NASDAQ:FFIV), VP Software Development chez Native Networks (acquis par Alcatel, NASDAQ: ALU) et a pris part à la fondation de la division ECI Telecom broadband, où il a officié en tant que VP Software Engineering. Yiftach a un B.S. en Mathématiques et Informatique et a terminé ses études par un Master's degree en Informatique à l'Université de Tel-Aviv.