La mauvaise façon de mettre à l’échelle Cassandra DB lorsque des index secondaires sont en place

Cassandra est ma base de données préférée (non gérée) pour de nombreuses raisons: elle n’a pas un seul point de défaillance (SPoF), prend en charge plusieurs régions, convient aux opérations de lecture et d’écriture, flexible sur les niveaux de cohérence de lecture et d’écriture, évolue linéairement et n’est pas trop complexe à gérer pour les opérations quotidiennes.

Comme chaque base de données, vous devez utiliser Cassandra en fonction de vos modèles d’accès aux données, donc si vous avez besoin d’une base de données flexible pour les requêtes ad hoc ou suffisamment adaptable pour des changements constants de modèle de base de données, vous devriez envisager d’autres options.

Cassandra est une base de données orientée colonnes et elle est vraiment puissante lorsque vos requêtes de données sont déjà définies. Datastax, la société qui prend en charge Cassandra, vous recommande de commencer par concevoir vos requêtes, puis votre modèle de données dans Cassandra. Malgré le fait que votre structure en colonnes, Cassandra prend en charge de nombreuses structures de données en tant que type de colonne (s), telles que les cartes.

Cassandra est une base de données de clés primaires, ce qui signifie que vos données sont conservées et organisées autour d’un cluster en fonction de la valeur de hachage (la clé de partition) de la clé primaire. Pour les tables qui ont plus d’un PK, Cassandra ne considère que la première partie du PK comme clé de partition. En savoir plus sur les clés composites ici.

Pour être plus clair, revenons à l’une des caractéristiques les plus importantes d’une base de données Cassandra: c’est l’architecture et le fait qu’elle n’ait pas de SPoF.

Un cluster Cassandra est composé de nœuds (3 ou plus) et ces nœuds forment ensemble un anneau de nœuds:

 Un cluster de Cassandra composé de six nœuds (n6)

 Un cluster de Cassandra composé de six nœuds (n6)

Un cluster Cassandra avec six nœuds (n6)

Chaque nœud du cluster d’un Cassandra fonctionne ” indépendamment “, mais différents nœuds peuvent stocker les mêmes données en fonction de la configuration du facteur de réplication (RF) configurée pour le cluster.

Pour savoir où (quel nœud) vos données sont conservées, Cassandra utilise la valeur de hachage (jeton) calculée via une fonction de hachage cohérente en utilisant la colonne PK d’une table donnée.

Lorsque vous exécutez une requête, le nœud coordinateur (normalement l’instance la plus proche de votre application) recherchera quels nœuds d’un anneau ont vos données, de cette façon si un nœud est en panne pour une raison quelconque, un autre nœud pourrait servir vos données (RF ≥2). C’est la magie d’une approche sans maître, où chaque nœud d’un anneau est égal en termes de lecture et d’écriture.

Ce concept de PK et de facteur de réplication est très important pour comprendre comment mettre à l’échelle votre cluster Cassandra lorsque votre application est dans des conditions de charge élevée.

Index secondaires

Cassandra a également le concept d’index secondaires. Dans les bases de données relationnelles, vous pouvez avoir de nombreux index dans une table donnée, le coût d’avoir un index secondaire est associé aux opérations d’écriture, pas aux opérations de lecture. Chez Cassandra, ce n’est pas vrai.

Les index secondaires dans Cassandra peuvent être utiles et tentants lorsque votre modèle de données a changé et que vous devez interroger en fonction d’une nouvelle colonne.

De cette façon, avec un index secondaire, vous pouvez exécuter une requête comme celle-ci:

SÉLECTIONNEZ * DE ma_table OÙ SECONDARY_INDEX = ‘ valeur’;

Le problème de l’utilisation d’un index secondaire

Imaginez le scénario: vous êtes dans un Blackfriday / CyberMonday et votre cluster Cassandra souffre d’événements de pointe et vous devez ajouter plus de nœuds pour faire évoluer votre base de données, équilibrer mieux votre trafic et surviving survivre. Bien, non?

Normalement, il s’agit d’une situation normale dans une application hautement évolutive. Mais qu’en est-il si votre application exécute des requêtes à l’aide d’un index secondaire ?

Oui, vous avez compris.

Rappelez-vous quand j’ai dit que Cassandra distribuait des données en anneau en utilisant la clé de partition? Cela se produit déjà, mais le problème est lorsque vous introduisez un index secondaire dans votre requête. Les index secondaires ne FONT PAS PARTIE d’une clé de partition, et Cassandra sait où vivent vos données à travers la clé de partition. Lorsque vous exécutez une requête qui utilise ce type d’index, Cassandra recherche chaque nœud de votre anneau en essayant de satisfaire votre requête.

Scénario réel

Lors d’un vendredi noir, nos applications étaient très chargées. De nombreux et nombreux clients souhaitant bénéficier des énormes réductions offertes par un événement Blackfriday.

Nous avons jeté un coup d’œil à notre APM et toute l’analyse nous a conduit à notre persistance, en l’occurrence une base de données de Cassandra. Nous avons eu de longues périodes de latence, mais pas pour toutes les demandes, juste pour certaines.

En essayant de ramener les choses à l’état normal, notre première manœuvre a été d’ajouter plus de nœuds à notre cluster Cassandra.

Nous avons ajouté et nous souffrons toujours de problèmes de latence. La question était: pourquoi cela se produit-il toujours?

Nous avions tort. C’était une conclusion simpliste et nous n’avons pas pris soin d’un détail très important: ce comportement ne se produisait pas dans toutes les demandes, mais dans certaines d’entre elles.

Si vous avez pensé à l’index secondaire, bingo! C’était exactement le problème.

L’ajout de nœuds ne résoudrait jamais le problème, car le problème n’était pas lié à toutes les requêtes arrivant dans la base de données, le problème était dans certaines et ce sont les vraies qui dégradaient les performances de la base de données. C’était totalement un truc de Pareto.

Détaillant le problème et la façon dont nous l’atténuons

À un moment avant l’événement Blackfriday, nous devions modifier notre modèle de données. Nous avons régionalisé notre application et la région du client a commencé à être une chose importante pour nous, nous devions interroger des données en fonction d’un produit OU d’une région.

En regardant en arrière et en reliant les points, nous avons pu réaliser que nous étions très précieux pour l’implémentation car nous voulions refléter ce nouveau comportement non seulement dans la couche API (nouveau paramètre de requête), mais aussi dans la façon dont nous accédions aux données dans Cassandra.

Et pourquoi étions-nous si précieux? Parce que même en considérant que notre temps de requête n’a pas beaucoup augmenté, nous avons fait le changement.

Cette implémentation a non seulement augmenté notre temps de requête en utilisant un index secondaire, mais a également généré plus de problèmes selon que nous avons agrandi l’infrastructure de Cassandra. Comme nous avons ajouté plus de nœuds dans notre cluster, cela signifiait plus de nœuds à rechercher pour trouver les données, le problème augmentait de manière exponentielle.

Pour atténuer le problème, nous avons repris le nombre de nœuds que nous avions précédemment et augmenté le facteur de réplication pour la majorité de nos nœuds du cluster.

Nous avons également modifié notre niveau de cohérence de lecture pour qu’il soit moins cohérent. Nous utilisions * QUORUM et à la place, nous avons changé pour UN. Cela nous a aidés à réduire la charge dans les nœuds.

Comme nous avons gelé nos applications quelques jours avant l’événement, nous savions que nous n’avions pas de nouvelles données (opérations d’écriture) et que les données seraient cohérentes dans leur état actuel.

Les jours suivants et la solution de modèle de base de données

Dans le cadre de la solution finale, nous devions (re)réfléchir à notre modèle de base de données et annuler les modifications que nous avons apportées en tant que chemin d’atténuation pendant l’événement.

Avant l’événement, nous utilisions l’ID de produit (PID) comme clé de partition, ce qui était une bonne décision, car le PID a de bons attributs pour être un PK en raison de sa nature d’être un nombre séquentiel (cardinalité élevée), et de cette façon, répartissant les données uniformément autour du cluster.

À propos du nouveau champ “région”, nous exploitons le type de données des collections Cassandra et utilisons une carte pour chaque région comme colonne dans notre tableau de produits.

Les index secondaires sont toujours une mauvaise idée?

La réponse courte est non.

Pour expliquer un peu mieux, il existe deux types d’index dans Cassandra: les index locaux et globaux.

Un index local comme son nom l’indique est une sorte d’index qui n’existe que localement, c’est-à-dire dans un nœud. Lorsque vous créez un index secondaire, Cassandra crée une nouvelle table (cachée) où la clé secondaire devient une clé primaire dans cette table. La visibilité de cette nouvelle table est en termes de nœud, pas d’anneau (cluster). C’est le cas des index secondaires.

D’autre part, un index global a une visibilité en anneau via sa clé de partition, de sorte que Cassandra sait où se trouvent vos données dans un anneau via cette clé de partition.

Les index secondaires peuvent être une alternative, lorsque vous avez dans une requête à la fois: des index primaires et secondaires. Dans ce cas, Cassandra sait où résident vos données (quel nœud) via la clé de partition, puis recherche la table locale dans le nœud qui fait référence aux index secondaires (locaux).

Il y a aussi d’autres nuances sur les index secondaires qui sont très bien expliquées ici, mais la meilleure pratique est de les éviter en dénormalisant votre modèle de données.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.