---
marp: true
header: Cassandra et les bases de données NoSQL
footer: 3SE • Matthieu Jolimaitre • 24/06/2025
---
<!-- paginate: true -->

<style>
.cent {
  display: flex;
  align-items: center;
  justify-content: space-around;
  margin: 2rem;
}

small {
  font-size: 0.75rem;
}

.cols {
  display: flex;
  align-items: center;
  justify-content: space-around;

}

</style>

# Cassandra et les bases de données NoSQL : architecture, performances et cas d’usage

Analyser les principes de Cassandra, ses avantages par rapport aux bases relationnelles, et ses cas d’application dans le Big Data.

> Matthieu Jolimaitre

<br>

<div class="cent">
  <img width="300" src="./diagrams/cassandra_logo.png">
  <img width="300" src="./diagrams/NoSQL_logo.jpg">
</div>

---

## Sommaire

<small>

- Introduction aux bases NoSQL
  - Pourquoi NoSQL ?
  - Catégories (clé-valeur, document, colonne, graphe)
- Architecture de Cassandra
  - Peer-to-peer, partitionnement, réplication
  - Consistency levels et CAP theorem
- Modèle de données et requêtage
  - Tables dénormalisées, CQL (Cassandra Query Language)
- Performances et cas d’usage
  - Écritures rapides, scalabilité horizontale
  - Cas d’étude
- Limites et comparaison avec d’autres NoSQL

</small>

---

## Introduction aux bases NoSQL

> Not Only SQL

![bg fit right](./diagrams/not_only_sql_venn.png)

---

#### Le SQL

Un modèle de donnée appuyé sur des tableaux et des scripts de modification.

<br>

```sql
INSERT INTO checkouts
  (id, user_id, book_id, checkout_date, return_date)
VALUES
  (1, 1, 1, '2017-10-15 14:43:18', NULL),
  (2, 1, 2, '2017-10-05 16:22:44', '2017-10-13 13:00:12'),
  (3, 2, 2, '2017-10-15 11:11:24', '2017-10-22 17:47:10'),
  (4, 5, 3, '2017-10-15 09:27:07', NULL);
```

![bg fit right](./diagrams/sql_table_preview.png)

---

### Pourquoi NoSQL ?

<div class="cols">

<div>

- Utiliser de nouvelles topologies.
  - Fournir des interfaces de modification plus adaptés à la donnée.
- Se détacher du modèle de données arrangées en tables.
  - Modéliser des états plus complexes.
  - Optimiser en fonction des données.

</div>

<div>

- Autoriser des transgressions du principe ACID.
  - Atomicité.
  - Consistance.
  - Isolation.
  - Durabilité.
- Apparition de nouveaux principes BASE.
  - Basiquement Disponible.
  - Souplesse de l'état.
  - Éventuellement consistent.

</div>

</div>

---

### Catégories

<small>

- **clé-valeur** : Les données sont des valeurs accessibles par leur clé (souvent un texte).
- **document** : Clé-valeur optimisé pour les données structurées.
- **colonne larges** : Les données sont mises en tableau, mais le schéma d’un même tableau peut varier selon les lignes.
- **graphe** : Les données sont arrangées dans des nœuds pouvant être référencés dans d’autres nœuds.

</small>

![bg fit right](./diagrams/nosql_categories.png)

---

## Architecture de Cassandra

- Suit un modèle de donnée en **Large colonnes**.
- **Distribuée** : Les données sont répliquées sur plusieurs 'nœuds' pour permettre une expansion horizontale et pour se rendre résilient aux pannes.
- Garanti une **consistance éventuelle**.

![bg fit right](./diagrams/cassandra_architecture.png)

---

### Peer-to-peer, partitionnement, réplication

Différentes techniques pour répartir et synchroniser les données dans un système distribué.

![bg fit right](./diagrams/nodes_gossip_example.png)

---

#### pair-à-pair

Pour synchroniser les changements de données, **la grappe n’utilise pas un nœud autoritaire** orchestrant la synchronisation.

À la place, les nœuds communiquent périodiquement par deux, sans hiérarchie, en utilisant un **protocole de 'potins'**.

![bg fit right](./diagrams/gossip_protocol.png)

---

#### Partitionnement

<small>

Pour réduire la charge d’une requête sur la grappe, les données sont partitionnées entre les différents nœuds.

C’est à dire qu’**une donnée n’apparaitra que sur 'certains' nœuds** de la grappe, et qu’une requête accédant à cette donnée devra être traitée par un de ces nœuds.

Pour une donnée partitionnée, l’adresse du nœud contenant une ligne est comprise **dans sa clé primaire**.

</small>

![bg fit right](./diagrams/partition_example.png)

---

#### Réplication

<small>

Pour diminuer le **risque de perte** de donnée et augmenter la **disponibilité** d’une donnée, la réplication sert à contenir la même donnée dans plusieurs nœuds.

Concrètement, les partitions ont chacune un **facteur de réplication** RF, et **une partition est copiée vers RF nœuds** différents à partir d’un nœud responsable de cette partition.

Ce système est implémenté avec un mécanisme de jeton.

</small>

![bg fit right](./diagrams/replication_example.jpg)

---

### Consistency levels et CAP theorem

Les bases de données distribués ont trois aspects propres :
- **Consistance** : Toutes les lectures d’une même donnée retournent la version la plus récente de cette donnée.
- **Disponibilité** : Maximiser le taux de réponse aux requêtes saines.
- **Tolérance au partitionnement** : La dégradation de certains nœuds n’entraine pas une dégradation du système.

---

### Consistency levels et CAP theorem

Le 'théorème de CAP' énonce qu’une base de donnée distribuée **doit se focaliser sur deux aspects**, car il est impossible d’en atteindre deux sans en sacrifier un.

![bg fit right](./diagrams/CAP_theorem_venn.png)

---

## Modèle de données et requêtage

Cassandra suit un modèle de **tables à colonnes larges**, les colonnes sont de taille et structure dynamique : Le même tableau peut avoir des lignes de colonnes différentes.

Cela augmente la **flexibilité de modélisation** et diminue les **efforts de mise à jour du schéma** d’un tableau.

![bg fit right](./diagrams/cassandra_data_model.png)

---

### Tables dénormalisées, CQL (Cassandra Query Language)

<small>

Le modèle de donnée est normalisé lorsqu'il réponds à plusieurs exigences :
- **UNF** : Aucune colonne n'est dupliquée.
- **1NF** : Les colonnes doivent contenir des valeurs primitives, et non des agrégats.
- **2NF** : Chaque colonne d’un tableau dépends de toutes les clés primaires de ce tableau.
- **3NF** : Les colonnes ne dépendent uniquement des clés primaires de leur tableau.

</small>

```
 Normalisé                         │ Dénormalisé
┌──────────────┐ ┌───────────────┐ │ ┌─────────────────────────┐ ┌───────────────┐
│USERS BY EMAIL│ │USERS          │ │ │USERS BY EMAIL           │ │USERS          │
├─────┬────────┤ ├──┬────────┬───┤ │ ├──┬─────┬────────┬───────┤ ├──┬────────┬───┤
│email│ user_id│ │id│    name│age│ │ │id│email│    name│user_id│ │id│    name│age│
├─────┼────────┤ ├──┼────────┼───┤ │ ├──┼─────┼────────┼───────┤ ├──┼────────┼───┤
│b@o.b│       1│ │ 1│     bob│ 19│ │ │ 1│b@o.b│     bob│      1│ │ 1│     bob│ 19│
│c@r.l│       2│ │ 2│ charlie│ 23│ │ │ 2│c@r.l│ charlie│      2│ │ 2│ charlie│ 23│
└─────┴────────┘ └──┴────────┴───┘ │ └──┴─────┴────────┴───────┘ └──┴────────┴───┘
```

---

- La dénormalisation :
  - Réduit le nombre d’opération par lecture.
  - Augmente le nombre d’opération par écriture.

<br>

```
 Normalisé                         │ Dénormalisé
┌──────────────┐ ┌───────────────┐ │ ┌─────────────────────────┐ ┌───────────────┐
│USERS BY EMAIL│ │USERS          │ │ │USERS BY EMAIL           │ │USERS          │
├─────┬────────┤ ├──┬────────┬───┤ │ ├──┬─────┬────────┬───────┤ ├──┬────────┬───┤
│email│ user_id│ │id│    name│age│ │ │id│email│    name│user_id│ │id│    name│age│
├─────┼────────┤ ├──┼────────┼───┤ │ ├──┼─────┼────────┼───────┤ ├──┼────────┼───┤
│b@o.b│       1│ │ 1│     bob│ 19│ │ │ 1│b@o.b│     bob│      1│ │ 1│     bob│ 19│
│c@r.l│       2│ │ 2│ charlie│ 23│ │ │ 2│c@r.l│ charlie│      2│ │ 2│ charlie│ 23│
└─────┴────────┘ └──┴────────┴───┘ │ └──┴─────┴────────┴───────┘ └──┴────────┴───┘
```
---

#### Cassandra Query Language : Un DSL pour Cassandra

```sql
/* Create a new keyspace in CQL */
CREATE KEYSPACE myDatabase WITH replication = 
   {'class': 'SimpleStrategy', 'replication_factor': 1};

/* Create a new database in SQL */
CREATE DATABASE myDatabase;
```
```sql
/* Expiring Data in 24 Hours */
INSERT INTO myTable (id, myField) VALUES (2, 9) USING TTL 86400;
```

- Les écritures sont peu coûteuses, donc il est encouragé de créer des tables redondantes plutôt que des requêtes complexes.
  - Le filtrage est désactivé par défaut.

---

## Performances et cas d’usage

- Distribué
  - **Répliqué** : Forte résilience.
  - **Partitionné** : Bonne mise à l’échelle.
- **NoSQL** : Flexibilité.
  - Modéliser sa donnée avec moins de contraintes.
  - Adapter les schémas pour améliorer les performances.

---

### Écritures rapides, scalabilité horizontale

Cassandra est une base de donnée optimisée pour la **mise à l’échelle**, c’est à dire offrir une grande disponibilité au détriment de la **consistance forte des données**.

Donc elle est recommandée dans les cas où il y a une haute contention d’écritures, et où la consistance de la donnée n'est pas cruciale.

![bg fit right](./diagrams/cassandra_scalability_benchmark.png)

---

### Cas d’étude : Discord

![bg right](./diagrams/discord_background.webp)


>  July, we announced 40 million messages a day, in December we announced 100 million. How do we do it? Cassandra!
>
> <br>
>
> https://discord.com/blog/how-discord-stores-billions-of-messages

---

#### Requirements

> We defined our requirements:
> 
> - **Linear scalability** : We do not want to reconsider the solution later or manually re-shard the data.
> - **Low maintenance** : It should just work once we set it up. We should only have to add more nodes as data grows.
> - **Predictable performance** : We have alerts go off when our API’s response time 95th percentile goes above 80ms. We also do not want to have to cache messages in Redis or Memcached.
> - **Not a blob store** : Writing thousands of messages per second would not work great if we had to constantly deserialize blobs and append to them.
> 
> Cassandra was the only database that fulfilled all of our requirements.

---

#### Distribution

> Here is a simplified schema for our messages table (this omits about 10 columns).
> 
> ```sql
> CREATE TABLE messages (
>   channel_id bigint, bucket int, message_id bigint, author_id bigint, content text,
>      PRIMARY KEY ((channel_id, bucket), message_id)
> ) WITH CLUSTERING ORDER BY (message_id DESC);
> ```

- Nous notons une clé de partitionnement composite, utilisant l’ID d’un salon ET un nombre arbitraire pour contrôler la taille des partitions.

---
### Résultats

![](./diagrams/discord_perf.png)

> At the beginning of 2022, it had 177 nodes with trillions of messages

---

## Limites et comparaison avec d’autres NoSQL

---

#### Faible consistance

- Une **consistance éventuelle** entraine un risque de **courses de données** pouvant rendre l’état du service invalide.

![bg fit right](./diagrams/race_condition.png)

---

#### Coût de la mise à l’échelle

![bg fit right](./diagrams/scaling_comparison.png)

- Les performances sur un seul noeud sont relativement basses.

---

## Fin
