Philosophie orientée requêtes
Apache Cassandra s'éloigne radicalement des systèmes relationnels en adoptant une approche où les chemins d'accès aux données dictent la structure physique des tables. Au lieu de normaliser les entités métier puis d'élaborer des requêtes dynamiques, l'ingénierie Cassandra impose de cartographier précisément les lectures attendues avant même de définir le schéma. Cette inversion des priorités garantit des performances prévisibles et une latence minimale, car la disposition des données sur les nœuds épouse exactement les filtres et les tris requis par l'application.
Architecture des clés et mécanisme de partitionnement
La pierre angulaire de cette stratégie réside dans la composition de la clé primaire (PRIMARY KEY). Celle-ci assume deux responsabilités distinctes :
- Clé de partition : Premier champ ou groupe de champs entre parenthèses. Son hachage détermine le nœud physique responsable du stockage. Cibler une seule partition lors d'une lecture élimine la coordination multi-nœuds et réduit drastiquement la surcharge réseau.
- Clés de clustering : Colonnes suivantes dans la définition. Elles ordonnent les enregistrements à l'intérieur d'une partition directement sur le disque, permettant des lectures séquentielles optimisées.
Exemple structurel : PRIMARY KEY ((zone_geo, ville), date_arrivee, ref_chambre) utilise zone_geo et ville pour le routage, tandis que date_arrivee et ref_chambre imposent un tri interne croissant ou décroissant.
Rupture avec le paradigme relationnel
- Absence de jointures et d'intégrité référentielle : Cassandra ne gère ni les opérations
JOINni les clés étrangères. Les relations doivent être résolues côté applicatif ou, plus efficacement, via la duplication stratégique des données. - Dénormalisation assumée : La redondance n'est pas un anti-pattern mais un levier de performance. Elle préserve également l'historique métier (ex: une facture doit figer l'adresse et le tarif au moment de la transaction, indépendamment des mises à jour futures).
- Tri figé à la création : Contrairement au SQL où
ORDER BYest évalué dynamiquement, l'ordre de tri dans Cassandra est immuable et défini par les colonnes de clustering lors duCREATE TABLE. Changer l'ordre nécessite une table dédiée. - Stockage orienté colonnes groupées : Les données sont persistées dans des fichiers SSTables. Regrouper les attributs fréquemment interrogés ensemble minimise les opérations d'I/O disque.
Méthodologie : du besoin métier au modèle physique
La conception débute par l'inventaire des accès critiques. Dans un contexte de gestion hôtelière, les parcours suivants illustrent la nécessité de multiplier les tables pour une même entité :
- Rechercher des établissements à proximité d'un lieu touristique.
- Obtenir la fiche descriptive d'un hôtel.
- Lister les attractions accessibles depuis un hébergement.
- Vérifier la disponibilité des chambres sur un intervalle de dates.
- Consulter les équipements associés à une chambre.
- Retrouver un dossier via son code de confirmation.
- Filtrer les réservations par hôtel et date d'arrivée.
- Extraire l'historique des séjours d'un voyageur.
- Accéder au profil complet d'un client.
Chaque parcours justifie la création d'une structure physique dédiée. Une réservation sera donc répliquée dans plusieurs tables, chacune optimisée pour un vecteur de recherche spécifique.
Dimensionnement et optimisation des partitions
Une partition excessivement large dégrade les performances de lecture et d'écriture. Bien que Cassandra autorise théoriquement 2 milliards de cellules par partition, les seuils pratiques recommandés sont bien inférieurs. Le volume se calcule ainsi :
Cellules = Lignes × (Colonnes_Totales - Colonnes_Clé_Primaire - Colonnes_Statiques) + Colonnes_Statiques
Si une partition risque de gonfler démesurément (ex: suivi de disponibilité sur plusieurs années), la technique du bucketing s'impose. On injecte un segment temporel ou logique (comme mois_annee) dans la clé de partition pour fragmenter les données en blocs équilibrés, tout en conservant la cohérence métier.
Implémentation complète en CQL
Les définitions ci-dessous matérialisent les concepts précédents. Les noms de colonnes, les types et la structure des clés ont été adaptés pour illustrer une implémentatino production-ready.
CREATE KEYSPACE gestion_hebergement WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};
CREATE TYPE gestion_hebergement.coordonnees (
voie text,
ville text,
region text,
code_postal text,
pays text
);
-- Parcours 1 : Recherche par lieu d'intérêt
CREATE TABLE gestion_hebergement.etablissements_par_lieu (
lieu_nom text,
ref_etablissement text,
denomination text,
telephone text,
localisation frozen<coordonnees>,
PRIMARY KEY ((lieu_nom), ref_etablissement)
) WITH CLUSTERING ORDER BY (ref_etablissement ASC);
-- Parcours 2 : Détails d'un établissement
CREATE TABLE gestion_hebergement.fiches_etablissements (
ref_etablissement text PRIMARY KEY,
denomination text,
telephone text,
localisation frozen<coordonnees>,
attractions_proches set<text>
);
-- Parcours 3 : Lieux proches d'un hôtel
CREATE TABLE gestion_hebergement.lieux_par_etablissement (
ref_etablissement text,
lieu_nom text,
descriptif text,
PRIMARY KEY ((ref_etablissement), lieu_nom)
);
-- Parcours 4 : Disponibilité quotidienne
CREATE TABLE gestion_hebergement.disponibilites_quotidiennes (
ref_etablissement text,
jour date,
numero_chambre smallint,
statut_libre boolean,
PRIMARY KEY ((ref_etablissement), jour, numero_chambre)
);
-- Parcours 5 : Équipements par chambre
CREATE TABLE gestion_hebergement.equipements_chambres (
ref_etablissement text,
numero_chambre smallint,
nom_equipement text,
details text,
PRIMARY KEY ((ref_etablissement, numero_chambre), nom_equipement)
);
CREATE KEYSPACE gestion_reservations WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};
CREATE TYPE gestion_reservations.coordonnees (
voie text,
ville text,
region text,
code_postal text,
pays text
);
-- Parcours 6 : Recherche par code confirmation
CREATE TABLE gestion_reservations.dossiers_par_code (
code_confirmation text PRIMARY KEY,
ref_etablissement text,
date_debut date,
date_fin date,
numero_chambre smallint,
ref_client uuid
);
-- Parcours 7 : Recherche par hôtel et date
CREATE TABLE gestion_reservations.dossiers_par_hotel_date (
ref_etablissement text,
date_debut date,
date_fin date,
numero_chambre smallint,
code_confirmation text,
ref_client uuid,
PRIMARY KEY ((ref_etablissement, date_debut), numero_chambre)
);
-- Parcours 8 : Recherche par nom client
CREATE TABLE gestion_reservations.historique_par_client (
nom_famille text,
ref_etablissement text,
date_debut date,
date_fin date,
numero_chambre smallint,
code_confirmation text,
ref_client uuid,
PRIMARY KEY ((nom_famille), ref_etablissement)
);
-- Parcours 9 : Profil client complet
CREATE TABLE gestion_reservations.profils_voyageurs (
ref_client uuid PRIMARY KEY,
prenom text,
nom_famille text,
civilite text,
courriels set<text>,
telephones list<text>,
adresses map<text, frozen<coordonnees>>,
dernier_code_confirmation text
);