Le Mauvais : Partitionnement
L’une des choses difficiles auxquelles il faut s’habituer au début est que sans index, les requêtes qui couvrent des lignes peuvent (très) être mauvaises. Cependant, en repensant à notre modèle de stockage, ce n’est pas surprenant. La stratégie utilisée par Cassandra pour répartir les lignes entre les hôtes s’appelle le partitionnement.
Le partitionnement est l’acte de découper la plage de clés de ligne en les affectant dans le « token ring », qui attribue également la responsabilité d’un segment (c’est-à-dire une partition) de la plage de clés de ligne à chaque hôte. Vous l’avez probablement vu lorsque vous avez initialisé votre cluster avec un « jeton ». Le jeton donne à l’hôte un emplacement le long de l’anneau à jetons, ce qui attribue la responsabilité d’une section de la plage de jetons. Le partitionnement est l’acte de mapper la clé de ligne dans la plage de jetons.
Il existe deux partitionneurs principaux: Aléatoire et Préservation de l’ordre. Ils sont nommés de manière appropriée. Le RandomPartitioner hache les clés de ligne en jetons. Avec le RandomPartitioner, le jeton est un hachage de la clé de ligne. Cela permet de répartir uniformément vos données sur un ensemble de nœuds, mais rend l’interrogation d’une plage de l’espace rowkey incroyablement difficile. À partir d’une valeur « start rowkey » et d’une valeur « end rowkey », Cassandra ne peut pas déterminer la plage de l’espace de jeton dont vous avez besoin. Il doit essentiellement effectuer une « analyse de table » pour répondre à la requête, et une « analyse de table » dans Cassandra est mauvaise car elle doit aller à chaque machine (très probablement TOUTES les machines si vous avez une bonne fonction de hachage) pour répondre à la requête.
Le bon: Index secondaires
Cassandra fournit un mécanisme d’indexation natif dans les index secondaires. Les index secondaires fonctionnent à partir des valeurs des colonnes. Vous déclarez un index secondaire sur une famille de colonnes. Datastax a une bonne documentation sur l’utilisation. Sous le capot, Cassandra maintient une « famille de colonnes cachées » comme index. (Voir la présentation d’Ed Anuff pour plus de détails) Puisque Cassandra ne conserve pas les informations de valeur de colonne dans un nœud et que les index secondaires sont sur la valeur des colonnes (plutôt que les clés de ligne), une requête doit toujours être envoyée à tous les nœuds. De plus, les index secondaires ne sont pas recommandés pour les ensembles à haute cardinalité. Je n’ai pas encore regardé, mais je suppose que c’est à cause du modèle de données utilisé dans la « famille de colonnes cachées ». Si la famille de colonnes masquées stocke une ligne par valeur unique (avec des clés de ligne en tant que colonnes), cela signifierait analyser les lignes pour déterminer si elles se trouvent dans la plage de la requête.
Extrait de la présentation d’Ed:
- Non recommandé pour les valeurs de cardinalité élevées (c’est-à-dire les horodatages, les dates de naissance, les mots-clés, etc.)
- Nécessite au moins une comparaison d’égalité dans une requêtenot pas génial pour les requêtes inférieures / supérieures à /range
- Non triées – Les résultats sont dans l’ordre des jetons, pas l’ordre des valeurs de requête
- Limité à la recherche sur les types de données, Cassandra comprend nativement
Cela dit, les index secondaires fonctionnent immédiatement et nous avons eu beaucoup de succès en les utilisant sur des valeurs simples.
Le moche : À faire soi-même (BRICOLAGE) / Rangées larges
Maintenant, la beauté est dans l’œil du spectateur. L’une des belles choses à propos de NoSQL est la simplicité. Les constructions sont simples : Espaces de clés, Familles de Colonnes, Lignes et Colonnes. Garder les choses simples signifie cependant parfois que vous devez prendre les choses en main.
C’est le cas des index à grandes lignes. En utilisant le modèle de stockage de Cassandra, il est facile de créer vos propres index où chaque clé de ligne devient une colonne dans l’index. C’est parfois difficile à comprendre, mais imaginons que nous ayons un cas dans lequel nous voulons sélectionner tous les utilisateurs dans un code postal. La famille de colonnes principale des utilisateurs est saisie sur l’ID utilisateur, le code postal est une colonne sur chaque ligne utilisateur. Nous pourrions utiliser des index secondaires, mais il y a pas mal de codes postaux. Au lieu de cela, nous pourrions maintenir une famille de colonnes avec une seule ligne appelée « idx_zipcode ». Nous pourrions alors écrire des colonnes dans cette ligne de la forme « zipcode_userid ». Étant donné que les colonnes sont stockées dans un ordre trié, il est rapide d’interroger toutes les colonnes qui commencent par « 18964 » (par exemple, nous pourrions utiliser 18964_ et 18964_ZZZZZZ comme valeurs de début et de fin).
Un inconvénient évident de cette approche est que les lignes sont autonomes sur un hôte. (encore une fois à l’exception des répliques) Cela signifie que toutes les requêtes vont frapper un seul nœud. Je n’ai pas encore trouvé de bonne réponse à cela.