View
1.046
Download
3
Category
Preview:
DESCRIPTION
DevoxxFR2014 Université sur les techniques de modélisation avancée dans Cassandra
Citation preview
@doanduyhai #C*ModelisationAvancee
Cassandra
Techniques de modélisation avancée
@doanduyhai #C*ModelisationAvancee
DuyHai DOAN
Le jour: + + conseil
Le soir: Cassandra + Achilles
@doanduyhai doanduyhai@gmail.com doanduyhai
Développeur Freelance Java/Cassandra
@doanduyhai #C*ModelisationAvancee
Agenda Modèle de données Architecture technique CQL3 en détail Techniques de modélisation de base
3
@doanduyhai #C*ModelisationAvancee
Agenda
Techniques de modélisation avancée Modélisations exotiques Les anti-patterns Les index secondaires <Futur> C* 4
@doanduyhai #C*ModelisationAvancee
MODÈLE DE DONNÉES
Concepts Accès aux données
Le type « composite »
@doanduyhai #C*ModelisationAvancee
Table/Column Family Stockage physique
ligne physique = partition
clé de partition (#partition)
clé de colonne (#colonne)
cellule
#partition1 #col1 #col2 #col3 #col4
cellule1 cellule2 cellule3 cellule4
#partition2 #col1 #col2 #col3
cellule1 cellule2 cellule3
#partition3 #col1 #col2
cellule1 cellule2
#partition4 #col1 #col2 #col3 #col4 …
cellule1 cellule2 cellule3 cellule4 …
6
@doanduyhai #C*ModelisationAvancee
Keyspace Ensemble de column family (table)
Keyspace system metadata sur les keyspaces et tables
Keyspace system_auth authentification & contrôle d’accès
Cassandra Keyspace Table 1 n 1 n
7
@doanduyhai #C*ModelisationAvancee
Distribution Aléatoire: hash de #partition → token = hash(#p) Hash: [0, 2127-1] Chaque nœud: 1/8
8
n1
n2
n3
n4
n5
n6
n7
n8
@doanduyhai #C*ModelisationAvancee
Table ≈ Map<#p,SortedMap<#col,cellule>>
Vue logique
SortedMap<tokenp,…>
9
@doanduyhai #C*ModelisationAvancee
Table ≈ Map<#p,SortedMap<#col,cellule>>
SortedMap<#col,cellule>>
Vue logique
Unicité
Tri
SortedMap<tokenp,…>
10
@doanduyhai #C*ModelisationAvancee
Vue logique Map<#p,SortedMap<#col,cellule>> → cellule par (#partition + #colonne) très rapide
#partition1 #col1 #col2 #col3 #col4 …
cellule1 cellule2 cellule3 cellule4 …
11
@doanduyhai #C*ModelisationAvancee
Vue logique Map<#p,SortedMap<#col,cellule>> → plage de #colonnes (triées) par #partition très rapide
#partition1 #col1 #col2 #col3 #col4 …
cellule1 cellule2 cellule3 cellule4 …
12
@doanduyhai #C*ModelisationAvancee
Vue logique Map<#p,SortedMap<#col,cellule>> → plage de partitions par #partition très lent
13
@doanduyhai #C*ModelisationAvancee
Vue logique Map<#p,SortedMap<#col,cellule>> Cellules vides Données possible dans #colonne
limite: 64K
#partition1 #col1 #col2 #col3 #col4
Ø Ø Ø Ø
14
@doanduyhai #C*ModelisationAvancee
Types Supportés
int, long, string, float, double, byte (blob) uuid, time uuid timestamp counter
#partition, #colonne & cellule Tri sur #colonne: ordre naturel du type
15
@doanduyhai #C*ModelisationAvancee
Démo
cassandra-cli : « stockage_physique »
@doanduyhai #C*ModelisationAvancee
Insertion
Chargement simple
Slice query
Accès aux données
17
INSERT INTO xxx(partition,colonne,cellule) VALUES(#p,#col,valeur)
SELECT cellule FROM xxx WHERE partition = #p AND colonne = #col
SELECT * FROM xxx WHERE partition = #p AND colonne ≥ #c1 AND colonne ≤ #c2 LIMIT n
@doanduyhai #C*ModelisationAvancee
Slice query
#partition = superhéros catwoman ≤ #col ≤ superman décroissant n = 3
Accès aux données
superhéros batman catwoman flash green lantern superman
Ø Ø Ø Ø Ø
3
18
@doanduyhai #C*ModelisationAvancee
Multiget slice query
slice query pour un ensemble de {#p1, #p2,…} donné équivalent à
→ #p1, entre: c1et c2 && → #p2, entre: c1et c2 && …
avantage / inconvénient
Accès aux données
19
@doanduyhai #C*ModelisationAvancee
#Colonne en multiple composants
type1:type2:type3 tri successif par composant
Type « composite »
20
@doanduyhai #C*ModelisationAvancee
#Colonne en multiple composants
type1:type2:type3 tri successif par composant
Map<#partition, SortedMap<#comp1,< SortedMap<#comp2,< … SortedMap<#compn,Cellule>…>
Type « composite »
21
@doanduyhai #C*ModelisationAvancee
Exemple: table hygrometrie #partition → ville #colonne → LongType:UTF8Type
LongType: date de mesure en Unix time UTF8Type: type de mesure
celulle → DoubleType
Map<ville,SortedMap<dateUnix,SortedMap<type,valeur>>>
Type « composite »
22
@doanduyhai #C*ModelisationAvancee
Démo
cassandra-cli : « hygrometrie »
@doanduyhai #C*ModelisationAvancee
Slice query avec « composite » Requêtes
relevé par ville, date et type #p = Paris, #comp1 = date1, #comp2 = temperature
relevés par ville et date #p = Paris, #comp1 = date1
relevés par ville et tranche de dates #p = Paris, #comp1 ≥ date1 & #comp1 ≤ date2
24
@doanduyhai #C*ModelisationAvancee
Slice query avec « composite » Requêtes
relevé par ville et type #p = Paris, #comp2 = temperature
Map<ville,SortedMap<date,SortedMap<type,valeur>>>
25
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
ARCHITECTURE TECHNIQUE
Système de fichiers Chemin de lecture/écriture
Compaction
@doanduyhai #C*ModelisationAvancee
Mémoire Off-Heap
Disque
Column Family User
Row Cache
Mémoire JVM Heap
Partition Key Cache
Commit log1
…
Key Index Sample
Compression offset
Bloom Filter
SStable 3
Système de fichiers
SStable 2 SStable 1 Commit log 2
Commit logn
Key Index Sample
Compression offset
Bloom Filter
Key Index Sample
Compression offset
Bloom Filter
28
@doanduyhai #C*ModelisationAvancee
Système de fichiers Format
<keyspace>-<column family>-<version>-<génération>-<composant>.db SSTables test-user-ic-1-Data.db → snapshot d’une SSTable à un instant t Méta-données test-user-ic-1-CompressionInfo.db → offset des données compressées test-user-ic-1-CRC.db → CRC des données non compressées
29
@doanduyhai #C*ModelisationAvancee
Système de fichiers Format
<keyspace>-<column family>-<version>-<génération>-<composant>.db Méta-données test-user-ic-1-Filter.db → filtre de Bloom test-user-ic-1-Index.db → index des offset de #partition dans le SSTable test-user-ic-1-Statistics.db → statistiques test-user-ic-1-Summary.db → échantillon des offsets de #partition test-user-ic-1-TOC.txt
30
@doanduyhai #C*ModelisationAvancee
Format des fichiers
#p (user001) Longueur ligne Index de colonne * Tombstone Nb de colonnes Colonne1/Cellule1
…. ColonneN/CelluleN
#p (user350) Longueur ligne Index de colonne * Tombstone Nb de colonnes Colonne1/Cellule1
…. ColonneN/CelluleN
#Partition Offset … Index de colonne *
user001 0x0 …
premier: xxx dernier: yyy offset: 0x3
…
user002 0x153 …
premier: xxx dernier: yyy
offset: 0x156 …
user350 0x5464321 …
premier: xxx dernier: yyy
offset: 0x5464310 …
*-Index.db *-Data.db
31
SStable Partition Key Cache
@doanduyhai #C*ModelisationAvancee
Format des fichiers
Echantillon Offset
user001 0x0
user128 0x4500
user256 0x851513
user350 0x5464321
*-Summary.db #p (user001) Longueur ligne Index de colonne * Tombstone Nb de colonnes Colonne1/Cellule1
…. ColonneN/CelluleN
#p (user350) Longueur ligne Index de colonne * Tombstone Nb de colonnes Colonne1/Cellule1
…. ColonneN/CelluleN
32
*-Data.db SStable
Key Index Sample
@doanduyhai #C*ModelisationAvancee
Format des fichiers
1 0 0 1 0 0 1 0 0 0
« foo »
h1 h2 h3
33
*-Filter.db Bloom Filter
@doanduyhai #C*ModelisationAvancee
Format des fichiers
1 0 0 1* 0 0 1 0 1 1
« foo » « bar »
h1 h2 h3
34
*-Filter.db Bloom Filter
@doanduyhai #C*ModelisationAvancee
Format des fichiers
1 0 0 1* 0 0 1 0 1 1
h1 h2 h3
« qux » ?
« foo » « bar »
35
*-Filter.db Bloom Filter
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture Mémoire
Disque2
Commit log1
Disque 1
. . .
1
Commit log2
Commit logn
36
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture Mémoire
Disque2
Commit log1
Disque 1
. . .
MemTable Column Family 1
. . .
1
2
Commit log2
Commit logn
MemTable Column Family 2
MemTable Column Family n
37
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture
Column Family1
Mémoire
Disque2
Commit log1
SStable 1
Disque 1
. . .
Column Family2
SStable 1
Column Family3
SStable 1
1
3 Commit log2
Commit logn
38
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture
Column Family1
Mémoire
Disque2
Commit log1
SStable 1
Disque 1
. . .
Column Family2
SStable 1
Column Family3
SStable 1
1
3 Commit log2
Commit logn
39
MemTable Column Family 1
. . . 2 MemTable
Column Family 2
MemTable Column Family n
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture
Column Family1
Mémoire
Disque2
Commit log1
SStable 1
Disque 1
. . . SStable 2
Column Family2
SStable 1
SStable 2
Column Family3
SStable 1
1
3 Commit log2
Commit logn
40
@doanduyhai #C*ModelisationAvancee
Chemin d’écriture
Column Family1
Mémoire
Disque2
Commit log1
SStable 1
Disque 1
. . . SStable 2
SStable 3
Column Family2
SStable 1
SStable 2
SStable 3
SStable 4 SStable 5
Column Family3
SStable 1
1
3
Immutables
Commit log2
Commit logn
41
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
1
42
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter 1 2
43
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
1 2
3
44
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
Key Index Sample 4
1 2
3
45
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
Compression offset
Key Index Sample 4
5
1 2
3
46
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
SStable 1
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
Compression offset
Key Index Sample 4
5
1
Column Family User
2
6
3
47
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Chemin de lecture
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
Compression offset
Key Index Sample 4
5
Memtable
1 2
7
∑
3
SStable 1
Column Family User
6
48
SELECT * FROM users WHERE partition =…
@doanduyhai #C*ModelisationAvancee
Mémoire Off-Heap
Disque
Column Family User
Row Cache
Mémoire JVM Heap
Partition Key Cache
Commit log1
…
Key Index Sample
Compression offset
Bloom Filter
SStable 3
Système de fichiers
SStable 2 SStable 1 Commit log 2
Commit logn
Key Index Sample
Compression offset
Bloom Filter
Key Index Sample
Compression offset
Bloom Filter
49
@doanduyhai #C*ModelisationAvancee
Gestion de conflits Données provenant de plusieurs SSTables
merge-sort en mémoire utilise timestamp → last-write win
SStable 1
Pk = xxx Col1,val11,time1 Col2,val21,time1
SStable 2
Pk = xxx Col1,val12,time2 Col3,val31,time3
SStable 3
Pk = xxx Col2,val22,time4 Col4,val41,time4
Pk = xxx Col1, val12,time2 Col2, val22,time4 Col3,val31,time3 Col4,val41,time4
50
@doanduyhai #C*ModelisationAvancee
Suppression de données SSTables immutables
pas de suppression physique
Suppression logique
écrire une colonne tombstone
51
@doanduyhai #C*ModelisationAvancee
Suppression de données SSTables immutables
pas de suppression physique
Suppression logique
écrire une colonne tombstone
A la lecture
comparaison timestamp tombstone + timestamp données
52
@doanduyhai #C*ModelisationAvancee
Compaction Beaucoup de SSTables
beaucoup d’accès disque → lent fragmentation des données
53
@doanduyhai #C*ModelisationAvancee
Compaction Beaucoup de SSTables
beaucoup d’accès disque → lent fragmentation des données
Purge des colonnes tombstone
54
@doanduyhai #C*ModelisationAvancee
Compaction Beaucoup de SSTables
beaucoup d’accès disque → lent fragmentation des données
Purge des colonnes tombstone Type de compaction
SizeTieredCompaction
LeveledCompaction
55
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
CQL3 EN DÉTAIL
CQL3 stockage physique Détails d’implémentation
Collections & Maps
@doanduyhai #C*ModelisationAvancee
Tables simples Schéma CQL3
CREATE TABLE users ( id bigint PRIMARY KEY, nom text, age int);
Stockage physique
#partition : LongType
#colonne: CompositeType(UTF8Type) cellule: ByteType
58
@doanduyhai #C*ModelisationAvancee
Démo
cassandra-cli : « users »
@doanduyhai #C*ModelisationAvancee
Colonne marqueur
RowKey: 10 => (column=, value=, timestamp=…) => (column=age, value=00000023, timestamp=…) => (column=nom, value=4a6f686e20444f45, timestamp=…)
60
@doanduyhai #C*ModelisationAvancee
Colonne marqueur
INSERT INTO USERS(id) VALUES(12)
RowKey: 10 => (column=, value=, timestamp=…) => (column=age, value=00000023, timestamp=…) => (column=nom, value=4a6f686e20444f45, timestamp=…)
RowKey: 10 => (column=, value=, timestamp=…)
61
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées) Schéma CQL3
62
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur text, contenu text, PRIMARY KEY (article_id, date_creation));
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées)
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur text, contenu text, PRIMARY KEY (article_id, date_creation));
#partition (1er)
#col d’agrégation/clustering (2ème … )
Schéma CQL3
63
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées) Stockage physique
#partition : IntType #colonne: CompositeType(DateType,UTF8Type) cellule: ByteType
Map<article_id, SortedMap<date_creation, SortedMap<nom_colonne,cellule>>>
64
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées) #partition : IntType #colonne: CompositeType(DateType,UTF8Type) cellule: ByteType
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur text, contenu text, PRIMARY KEY (article_id, date_creation));
Map<article_id, SortedMap<date_creation, SortedMap<nom_colonne,cellule>>>
65
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées)
article_id | date_creation | auteur | contenu ----------------+-----------------------------------+--------------------+-------------------------------- 1 | 2014-04-16 10:00:00| Dupond| Cet article est génial 1 | 2014-04-16 10:01:00| Martin| Très bon article
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur text, contenu text, PRIMARY KEY (article_id, date_creation));
Map<article_id, SortedMap<date_creation, SortedMap<nom_colonne,cellule>>>
66
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées)
RowKey: 1 => (column=2014-04-16 10:00:00+0100:, value=,…) => (column=2014-04-16 10:00:00+0100:auteur, value=‘Dupond’, …) => (column=2014-04-16 10:00:00+0100:contenu, value=‘Cet article est génial’,…) => (column=2014-04-16 10:01:00+0100:, value=,…) => (column=2014-04-16 10:01:00+0100:auteur, value=‘Martin’, …) => (column=2014-04-16 10:01:00+0100:contenu, value=‘Très bon article’,…)
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur text, contenu text, PRIMARY KEY (article_id, date_creation));
Map<article_id, SortedMap<date_creation, SortedMap<nom_colonne,cellule>>>
67
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées) #partition : UTF8Type #colonne: CompositeType(UTF8Type,IntType,UTF8Type) cellule: ByteType
Map<operateur, SortedMap<ville, SortedMap<date, SortedMap<nom_colonne,cellule>>>>
CREATE TABLE qualite_reseau ( operateur text, ville text, date int, // format YYYYmmDDhh qualite double, PRIMARY KEY (operateur,ville,date));
68
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées)
operateur | ville | date | qualité ----------------+----------+---------------------+------ Orange | Lyon | 2014041612 | 0.93 Orange | Paris | 2014041612 | 0.89 Orange | Paris | 2014041613 | 0.91 SFR | Lyon | 2014041612 | 0.88 SFR | Paris | 2014041612 | 0.78 SFR | Paris | 2014041615 | 0.93
CREATE TABLE qualite_reseau ( operateur text, ville text, date int, // format YYYYmmDDhh qualite double, PRIMARY KEY (operateur,ville,date));
Map<operateur, SortedMap<ville, SortedMap<date, SortedMap<nom_colonne,cellule>>>>
69
@doanduyhai #C*ModelisationAvancee
Tables « clustered » (agrégées) CREATE TABLE qualite_reseau ( operateur text, ville text, date int, // format YYYYmmDDhh qualite double, PRIMARY KEY (operateur,ville,date));
RowKey: Orange => (column=Lyon:2014041612:qualite, value=0.93, timestamp=…) => (column=Paris:2014041612:qualite, value=0.89, timestamp=…) => (column=Paris:2014041613:qualite, value=0.91, timestamp=…) ------------------- RowKey: SFR => (column=Lyon:2014041612:qualite, value=0.88, timestamp=…) => (column=Paris:2014041612:qualite, value=0.78, timestamp=…) => (column=Paris:2014041615:qualite, value=0.93, timestamp=…)
Map<operateur, SortedMap<ville, SortedMap<date, SortedMap<nom_colonne,cellule>>>>
70
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
Pourquoi CQL3 ? Retour du schéma (schemaless)
72
@doanduyhai #C*ModelisationAvancee
Pourquoi CQL3 ? Retour du schéma (schemaless) Découpler vue logique/stockage physique
73
@doanduyhai #C*ModelisationAvancee
Pourquoi CQL3 ? Retour du schéma (schemaless) Découpler vue logique/stockage physique API/requête plus familière & facile à prendre en main
74
@doanduyhai #C*ModelisationAvancee
Pourquoi CQL3 ? Retour du schéma (schemaless) Découpler vue logique/stockage physique API/requête plus familière & facile à prendre en main Pensez aux ops !!!!
75
@doanduyhai #C*ModelisationAvancee
La clause « WHERE » S’applique aux clés des SortedMap (unicité)
76
@doanduyhai #C*ModelisationAvancee
La clause « WHERE » S’applique aux clés des SortedMap (unicité) Autorise des recherches par plage (tri)
77
@doanduyhai #C*ModelisationAvancee
La clause « WHERE » S’applique aux clés des SortedMap (unicité) Autorise des recherches par plage (tri) Ordre des clés très important!!!!
SELECT * FROM qualite_reseau WHERE operateur = ‘ORANGE’ AND ville = ‘Paris’ AND date ≥ 2014041619 AND date ≤ 2014041621
SELECT * FROM qualite_reseau WHERE operateur = ‘ORANGE’ AND date ≥ 2014041619 AND date ≤ 2014041621
78
@doanduyhai #C*ModelisationAvancee
Les méta-données de CQL3 Noms des colonnes « clustered » dans les tables ? key_aliases | key_validator | column_aliases | comparator --------------------+----------------------+--------------------------+------------------------------------------------------------------------------- ["operateur"] | UTF8Type | ["ville","date"] | CompositeType(UTF8Type,Int32Type,UTF8Type)
SELECT key_aliases,key_validator,column_aliases,comparator FROM system.schema_columnfamilies WHERE keyspace_name=‘xxx' AND columnfamily_name=‘qualite_reseau’
79
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Pourquoi ne pas tout stocker dans les #colonnes ?
80
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Pourquoi ne pas tout stocker dans les #colonnes ?
RowKey: SFR => (column=Lyon:2013110512:qualite, value=0.88)
RowKey: SFR => (column=Lyon:2013110512:qualite:0.88, value=)
81
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Pourquoi ne pas tout stocker dans les #colonnes ?
limite des 64K pour les #colonnes colonnes counter, type spécial
RowKey: SFR => (column=Lyon:2013110512:qualite, value=0.88)
RowKey: SFR => (column=Lyon:2013110512:qualite:0.88, value=)
82
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Pourquoi ne pas tout stocker dans les #colonnes ?
limite des 64K pour les #colonnes colonnes counter, type spécial → counter colonne d’agrégation
RowKey: SFR => (column=Lyon:2013110512:qualite, value=0.88)
RowKey: SFR => (column=Lyon:2013110512:qualite:0.88, value=)
83
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Eager fetching par slice query
CREATE TABLE fichiers ( user_id bigint, file_id timeuuid, nom text, payload blob, taille int, PRIMARY KEY(user_id, file_id));
84
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Eager fetching par slice query
85
SELECT nom, taille FROM file WHERE user_id = 10 ORDER BY file_id DESC;
@doanduyhai #C*ModelisationAvancee
Détails d’implémentation Eager fetching par slice query
10 uuid1: uuid1:nom uuid1:payload uuid1:taille uuid2: … Ø video.mp4 xxxxxxxxxxxx 9Mb Ø
86
SELECT nom, taille FROM file WHERE user_id = 10 ORDER BY file_id DESC;
@doanduyhai #C*ModelisationAvancee
Collection & Map CREATE TABLE collections_maps ( id int PRIMARY KEY, amis list<text>, suiveurs set<text>, adresse map<text,text>) ;
INSERT INTO collections_maps(id,amis,suiveurs, adresse) VALUES(10,[‘Dupond’,’Martin’],{‘Jean’,’Jacques’},{‘pays’:’FR’,’ville’:’Paris’});
87
@doanduyhai #C*ModelisationAvancee
Collection & Map RowKey: 10 => (column=, value=, timestamp=…) => (column=amis:’uuid1’, value=‘Dupond’, timestamp=…) => (column=amis:’uuid2’, value=‘Martin’, timestamp=…) => (column=adresse:’pays’, value=‘FR’, timestamp=…) => (column=adresse:’ville’, value=‘Paris’, timestamp=…) => (column=suiveurs:’Jean’, value=, timestamp=…) => (column=suiveurs:’Jacques’, value=, timestamp=…)
88
@doanduyhai #C*ModelisationAvancee
Collection & Map Liste: => (column=amis:’uuid1’, value=‘Dupond’, timestamp=…) => (column=amis:’uuid2’, value=‘Martin’, timestamp=…) SortedMap<nom_de_colonne,SortedMap<uuid, valeur>>
Ordre
89
@doanduyhai #C*ModelisationAvancee
Collection & Map Set: => (column=suiveurs:’Jean’, value=, timestamp=…) => (column=suiveurs:’Jacques’, value=, timestamp=…) SortedMap<nom_de_colonne,SortedMap<valeur,Ø>>
Unicité
90
@doanduyhai #C*ModelisationAvancee
Collection & Map Map: => (column=adresse:’pays’, value=‘FR’, timestamp=…) => (column=adresse:’ville’, value=‘Paris’, timestamp=…) SortedMap<nom_de_colonne,SortedMap<clé,valeur>>
91
@doanduyhai #C*ModelisationAvancee
Collection & Map Set & Map
insertion et suppression d’élément(s)
92
UPDATE xxx SET suiveurs = suiveurs + {‘Pierre’}
UPDATE xxx SET adresse = adresse + {‘rue’ : ’rue de la paix’}
UPDATE xxx SET adresse[‘ville’] = ’Lyon’
@doanduyhai #C*ModelisationAvancee
Collection & Map Liste
ajout d’élément(s) en tête/en queue
93
UPDATE xxx SET amis = amis + [‘Pierre’]
UPDATE xxx SET amis = [‘Pierre’] + amis
@doanduyhai #C*ModelisationAvancee
Collection & Map Liste
ajout d’élément(s) en tête/en queue
modification/suppression d’élément à l’index i
insertion d’élément à l’index i
94
UPDATE xxx SET amis = amis + [‘Pierre’]
UPDATE xxx SET amis = [‘Pierre’] + amis
UPDATE xxx SET amis[1] = ‘Paul’
UPDATE xxx SET amis[1] = null
@doanduyhai #C*ModelisationAvancee
Collection & Map
1262304000000 01/01/2012 1383084226280
Append1 REFERENCE TIME
Liste prepend/append
95
@doanduyhai #C*ModelisationAvancee
Collection & Map
1262304000000 01/01/2012 1383084226280
Prepend2
-xxxxxxx
Append1
now()
REFERENCE TIME
96
Liste prepend/append
@doanduyhai #C*ModelisationAvancee
Collection & Map
1262304000000 01/01/2012 1383084226280
Append3 Prepend2
-xxxxxxx
Append1
now()
REFERENCE TIME
97
Liste prepend/append
@doanduyhai #C*ModelisationAvancee
Chargement
complet taille recommandée ≈ [100-1000]
Collection & Map
98
@doanduyhai #C*ModelisationAvancee
Chargement
complet taille recommandée ≈ [100-1000]
Index secondaire possible (C* 2.1) Requête avec ‘CONTAINS’ (C* 2.1)
SELECT … FROM collections_maps WHERE amis CONTAINS ‘Marc’
Collection & Map
99
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
TECHNIQUES DE MODÉLISATION DE BASE
De SQL vers Cassandra Dé-normalisation et compromis
Gérer la mutabilité Pièges & bonnes pratiques
@doanduyhai #C*ModelisationAvancee
1-1, n-1
normalisé
De SQL à Cassandra
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_id uuid, contenu text, PRIMARY KEY(article_id, date_creation));
Personne
1
écrit
N
Commentaire
102
@doanduyhai #C*ModelisationAvancee
1-1, n-1
dé-normalisé
De SQL à Cassandra
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_data text, //JSON ? contenu text, PRIMARY KEY(article_id, date_creation));
1
écrit
N
Commentaire
Personne
103
@doanduyhai #C*ModelisationAvancee
1-n, n-n
normalisé
De SQL à Cassandra
CREATE TABLE amis ( user_id uuid, ami_id uuid, PRIMARY KEY(user_id, ami_id)) ;
Personne N
est ami avec
N
104
@doanduyhai #C*ModelisationAvancee
1-n, n-n
dé-normalisé
De SQL à Cassandra
CREATE TABLE amis ( user_id uuid, ami_id uuid, ami_data text, // JSON ? PRIMARY KEY(user_id, ami_id)) ;
Personne N
est ami avec
N
105
@doanduyhai #C*ModelisationAvancee
1-1, n-1
compact
avantages/inconvénients
Type de dé-normalisation
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_data text, //JSON ? contenu text, PRIMARY KEY(article_id, date_creation));
106
@doanduyhai #C*ModelisationAvancee
1-1, n-1
éclaté
avantages/inconvénients
Type de dé-normalisation
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_id uuid, auteur_nom text, auteur_bio text, … contenu text, PRIMARY KEY(article_id, date_creation));
107
@doanduyhai #C*ModelisationAvancee
n-1, n-n
compact avec collections & maps
égalité code vs égalité C* !!!
Type de dé-normalisation
CREATE TABLE amis ( user_id uuid, amis set<text>, // JSON ? PRIMARY KEY(user_id)) ;
108
@doanduyhai #C*ModelisationAvancee
n-1, n-n
éclaté avec table « clustered »
Type de dé-normalisation
CREATE TABLE amis ( user_id uuid, ami_id uuid, ami_nom text, ami_bio text, … PRIMARY KEY(user_id, ami_id)) ;
109
@doanduyhai #C*ModelisationAvancee
Gérer la mutabilité
110
@doanduyhai #C*ModelisationAvancee
L’éliminer
Gérer la mutabilité
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_id uuid, auteur_login text, auteur_bio text, auteur_localisation text, … contenu text, PRIMARY KEY(article_id, date_creation));
111
@doanduyhai #C*ModelisationAvancee
Approche CQRS*
Gérer la mutabilité
CREATE TABLE amis ( user_id uuid, ami_id uuid, ami_nom text, ami_bio text, … PRIMARY KEY(user_id, ami_id)) ;
112
* Command Query Responsability Segregation
@doanduyhai #C*ModelisationAvancee
Approche CQRS*
Gérer la mutabilité
CREATE TABLE amis ( user_id uuid, ami_id uuid, ami_nom text, ami_bio text, … PRIMARY KEY(user_id, ami_id)) ;
Process M.A.J de la bio des users
113
* Command Query Responsability Segregation
@doanduyhai #C*ModelisationAvancee
Les pièges de la dé-normalisation Ne pas assez dé-normaliser
problème du N+1 select/read-before-write
100 commentaires → 101 selects
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_id uuid, contenu text, PRIMARY KEY(article_id, date_creation));
114
@doanduyhai #C*ModelisationAvancee
Les pièges de la dé-normalisation Compromis fonctionnels !
pagination par 10 commentaires max 11 selects
CREATE TABLE commentaires ( article_id uuid, date_creation timestamp, auteur_id uuid, contenu text, PRIMARY KEY(article_id, date_creation));
115
@doanduyhai #C*ModelisationAvancee
Les pièges de la dé-normalisation Trop dé-normaliser
intégrité des données mises à jour complexes batchs de correction de données …. cauchemardesques
116
@doanduyhai #C*ModelisationAvancee
Les pièges de la dé-normalisation Se couvrir fonctionnellement !!!
BDD, test d’acceptance tests unitaires
117
@doanduyhai #C*ModelisationAvancee
Les pièges de la dé-normalisation
Given 2 users Foo and Bar When Foo adds Bar as friend Then Foo friends list should display Bar’s details Given 2 users Foo and Bar When Foo adds Bar as friend And Bar changes his bio Then Foo should see new Bar’s bio in his friends list Given 2 users Foo and Bar When Foo adds Bar as friend And Bar deletes his account Then Foo should not see Bar in his friend list
118
@doanduyhai #C*ModelisationAvancee
TODO
approche query-first
Bonnes pratiques
119
@doanduyhai #C*ModelisationAvancee
TODO
approche query-first TDD & BDD compromis fonctionnels CQRS
Bonnes pratiques
120
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
@doanduyhai #C*ModelisationAvancee
TECHNIQUES DE MODÉLISATION AVANCÉE
Types de clés Ordre d’agrégation
La clause IN Wide row et « bucketing »
Le type counter
@doanduyhai #C*ModelisationAvancee
Clés de partition Clé de partition simple
CREATE TABLE xxx ( partition uuid, clustering1 uuid, clustering2 uuid, …., PRIMARY KEY(partition, clustering1,clustering2));
124
@doanduyhai #C*ModelisationAvancee
Clés de partition Clé de partition « composite »
CREATE TABLE xxx ( partition1 uuid, partition2 text, clustering1 uuid, clustering2 uuid, ….,
PRIMARY KEY((partition1, partition2), clustering1, clustering2));
#partition: CompositeType(UUIDType,UTF8Type) #colonne: CompositeType(UUIDType,UUIDType) cellule: ByteType
125
@doanduyhai #C*ModelisationAvancee
Clés de partition Clé de partition « composite »
CREATE TABLE xxx ( partition1 uuid, partition2 text, clustering1 uuid, clustering2 uuid, ….,
PRIMARY KEY((partition1, partition2), clustering1, clustering2));
126
@doanduyhai #C*ModelisationAvancee
Clés d’agrégation Pseudo « GROUP BY » Map<partition,
SortedMap<clustering1, SortedMap<clustering2,cellule>>>
PRIMARY KEY(partition, clustering1,clustering2);
127
@doanduyhai #C*ModelisationAvancee
Clés d’agrégation Unicité par tuple
128
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, commentaire text,
PRIMARY KEY(film_id, note));
@doanduyhai #C*ModelisationAvancee
Clés d’agrégation Unicité par tuple
129
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, commentaire text,
PRIMARY KEY(film_id, note)); 1 note/film
@doanduyhai #C*ModelisationAvancee
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, commentaire text, PRIMARY KEY(film_id, note, user_id));
Clés d’agrégation Unicité par tuple
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, commentaire text,
PRIMARY KEY(film_id, note)); 1 note/film
1 note/user
130
@doanduyhai #C*ModelisationAvancee
Ordre d’agrégation Tri descendant naturel
CREATE TABLE timeline ( user_id uuid, tweet_id timeuuid, tweet text, PRIMARY KEY (user_id, tweet_id)) WITH CLUSTERING ORDER (tweet_id DESC);
131
@doanduyhai #C*ModelisationAvancee
Ordre d’agrégation Tri descendant naturel
peu efficace, raison ? → scan en arrière
10 uuid1 uuid2 … uuid98 uuid99 uuid100 … … … … … … 33 uuid1 uuid2 … uuid51 uuid52
… … … … … … … …
132
@doanduyhai #C*ModelisationAvancee 133
Sur clé de partition « composite »
CREATE TABLE xxx ( partition1 uuid, partition2 text, …. PRIMARY KEY((partition1, partition2));
SELECT * FROM xxx WHERE partition1 = #p1 AND partition2 IN (#p21, #p22, …)
La clause « IN »
SELECT * FROM xxx WHERE partition1 IN (#p11, #p12, …) AND partition2 = #p2
@doanduyhai #C*ModelisationAvancee 134
Sur clé d’agrégation
CREATE TABLE xxx (partition uuid, clustering1 uuid, clustering2 uuid, clustering3 uuid …., PRIMARY KEY(partition, clustering1, clustering2, clustering3));
La clause « IN »
SELECT … WHERE clustering1 = #c1 AND clustering2 IN (#c21, #c22, …)
SELECT … WHERE clustering1 = #c1 AND clustering2 = #c2 AND clustering3 IN (#c31, #c32, …)
SELECT … WHERE clustering1 = #c1 AND clustering2 IN (#c21, #c22, …) AND clustering3 = #c3
@doanduyhai #C*ModelisationAvancee 135
Comment ça marche ? → multiget !
La clause « IN »
@doanduyhai #C*ModelisationAvancee 136
Comment ça marche ? → multiget !
Combinaison linéaire
(#p1, #p21) && (#p1, #p22)
La clause « IN »
SELECT * FROM xxx WHERE partition1 = #p1 AND partition2 IN (#p21, #p22, …)
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing » Lignes trop larges
limite physique 2.109 hot-spots si nb #partition « nb #col réparation coûteuse → 1colonne diffère, toute la ligne réparée
137
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing » Bucketing
découper une ligne physique sur plusieurs partitions répartit la charge attention aux requêtes!
138
@doanduyhai #C*ModelisationAvancee
Démo
cqlsh: « metrics_annee », « metrics_mois »
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing »
Requêtes inter-partition
toutes les métriques CPU entre 25 Janvier et 10 Février ?
CREATE TABLE metrics_mois ( type text, mois int, date timestamp, valeur double, PRIMARY KEY ((type, mois), date)) ;
140
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing »
Requêtes inter-partition
toutes les métriques CPU entre 25 Janvier et 10 Février ?
CREATE TABLE metrics_mois ( type text, mois int, date timestamp, valeur double, PRIMARY KEY ((type, mois), date)) ;
SELECT * FROM metrics_mois WHERE type = ‘CPU’ AND mois IN (201401, 201402) AND date>=‘2014-01-25 00:00:00+0100’ AND date<=‘2014-02-10 00:00:00+0100’;
141
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing » Implémentation par multiget
SELECT * FROM metrics_mois WHERE type = ‘CPU’ AND mois = 201401 AND date>=‘2014-01-25 00:00:00+0100’ AND date<=‘2014-02-10 00:00:00+0100’
&&
SELECT * FROM metrics_mois WHERE type = ‘CPU’ AND mois = 201402 AND date>=‘2014-01-25 00:00:00+0100’ AND date<=‘2014-02-10 00:00:00+0100’
142
@doanduyhai #C*ModelisationAvancee
2014-02-01 00:00:00 … 2014-02-10
23:59:59 …
0.24 … 0.23 …
Wide rows et « bucketing » Requêtes inter-partition
CPU:201401 2014-01-01
00:00:00 2014-01-01
00:00:10 … 2014-01-25 00:00:00 … 2014-01-31
23:59:59 0.24 0.23 … 0.27 … 0.25
25 Jan. – 31Jan.
01 Fev. – 10 Fev.
143
CPU:201402
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing » Pré-requis
valeur de #col de croissance monotone entre les partitions date/timeuuid bons candidats
144
@doanduyhai #C*ModelisationAvancee
Wide rows et « bucketing » Contre-exemple
CPU:2013 01 02 03 04 05 06 07 08 09 10 11 12 0.75 0.71 0.68 0.79 0.69 0.73 0.75 0.77 0.83 0.78 0.79 0.88
CPU:2014 01 02 03 04 05 06 07 08 09 10 11 12 0.71 0.72 0.67 0.80 0.71 0.75 0.74 0.76 0.81 0.79 0.83 0.87
SELECT moyenne FROM metrics_annee WHERE type = ‘CPU’ AND annee IN (2013, 2014) AND mois ≥ 11AND mois ≤ 02;
145
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
Le type counter Fonctionnement
seulement incrément/décrément suppression possible ne jamais réutiliser un compteur supprimé
Limitations
non idempotent pas 100% précis (ok pour analytics)
147
@doanduyhai #C*ModelisationAvancee
Les patterns avec counter / vote
naïve: incr/decr 1 seul compteur version duale: pour/contre infos étendues
total: pour + contre ratio: pour/(pour + contre)
CREATE TABLE popularite ( article_id uuid PRIMARY KEY pour counter, contre counter) ;
148
@doanduyhai #C*ModelisationAvancee
Les patterns avec counter Système de rating
CREATE TABLE film_rating ( film_id uuid PRIMARY KEY une_etoile counter, deux_etoiles counter, trois_etoiles counter, quatre_etoiles counter, cinq_etoiles counter) ;
149
@doanduyhai #C*ModelisationAvancee
Démo
cqlsh : « abonnes_telecom », « abonnes_telecom_par_localite »
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
TECHNIQUES DE MODÉLISATION EXOTIQUES
Time To Live FTW Timestamp, WTF ?
C.A.S. distribué
@doanduyhai #C*ModelisationAvancee
Time To Live En bref
données volatiles/temporaires seulement sur colonnes résolution en seconde expiré côté serveur
153
@doanduyhai #C*ModelisationAvancee
Time To Live Jetons de sécurité
CREATE TABLE oauth_tokens( token uuid PRIMARY KEY, user_id int, permissions set<text>) ;
154
@doanduyhai #C*ModelisationAvancee
Time To Live Jetons de sécurité Jetons à usage unique
CREATE TABLE oauth_tokens( token uuid PRIMARY KEY, user_id int, permissions set<text>) ;
CREATE TABLE code_de_validation( code_de_validation text PRIMARY KEY, email text) ;
155
@doanduyhai #C*ModelisationAvancee
Time To Live Fonction ttl() dans CQL3
156
SELECT ttl(code_de_validation) FROM code_de_validation WHERE code_de_validation=‘sdfkl0rf’;
@doanduyhai #C*ModelisationAvancee
Time To Live Rate limiting
CREATE TABLE rate_limiting ( user_id int, tentative uuid, PRIMARY KEY (user_id, tentative)) ;
157
@doanduyhai #C*ModelisationAvancee
Time To Live Rate limiting
CREATE TABLE rate_limiting ( user_id int, tentative uuid, PRIMARY KEY (user_id, tentative)) ;
SELECT COUNT(*) FROM rate_limiting WHERE user_id =10
158
@doanduyhai #C*ModelisationAvancee
Démo
Rate Limiting
Source: bit.ly/1noCqY1
@doanduyhai #C*ModelisationAvancee
Timestamp En bref
automatique sur chaque colonne insérée valeur modifiable par client résolution en micro-seconde utilisé pour résolution de conflit
160
@doanduyhai #C*ModelisationAvancee
Démo
Timestamp Barrière d’écriture
Source: bit.ly/1noCqY1
@doanduyhai #C*ModelisationAvancee
C.A.S. distribué Lightweight transaction
depuis C* 2.0 implémentation de Paxos très coûteux (3 aller/retours) ! utile pour 1% use-cases
162
@doanduyhai #C*ModelisationAvancee
C.A.S. distribué Contrainte d’intégrité
UPDATE abonnes SET preferences=… WHERE identifiant=… IF compte_valide=true
163
@doanduyhai #C*ModelisationAvancee
C.A.S. distribué Contrainte d’intégrité Contrainte d’unicité
UPDATE abonnes SET preferences=… WHERE identifiant=… IF compte_valide=true
INSERT INTO abonnes(…) VALUES(…) IF NOT EXISTS
164
@doanduyhai #C*ModelisationAvancee
Démo
cqlsh : « lock »
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
LES ANTI-PATTERNS
Queues et suppression Détection de présence/d’absence
Read-before-write Row cache
@doanduyhai #C*ModelisationAvancee
Queues Implémentation naïve
a b c d e
Queue FIFO C*
a b c d e
168
@doanduyhai #C*ModelisationAvancee
Queues Implémentation naïve
a b c d e
b c d e
Queue FIFO C*
a b c d e
a b c d e a
169
@doanduyhai #C*ModelisationAvancee
Queues Implémentation naïve
a b c d e
Queue FIFO C*
a b c d e
a b c d e a
b c d e f a b c d e a f
170
b c d e
@doanduyhai #C*ModelisationAvancee
Queues Implémentation naïve
a b c d e
Queue FIFO C*
a b c d e
a b c d e a
b c d e f a b c d e a f
c d e f a b c d e a f b
171
b c d e
@doanduyhai #C*ModelisationAvancee
Queues Queue FIFO consommée
a b c d e a f b c d e f
172
@doanduyhai #C*ModelisationAvancee
Queues Queue FIFO consommée
Queue avec rollback
a b c d e a f b c d e f
a a a a a a a a a a … a
173
@doanduyhai #C*ModelisationAvancee
Queues Queue FIFO consommée
Queue avec rollback Limite 100K tombstones → leveled compaction ≈ Partition trop large!
a b c d e a f b c d e f
a a a a a a a a a a … a
174
@doanduyhai #C*ModelisationAvancee
Queues: solutions Utiliser JMS
best tool for the job !!!!
175
@doanduyhai #C*ModelisationAvancee
Queues: solutions Utiliser JMS
best tool for the job !!!!
Besoin de distribution ?
Apache Kafka
176
@doanduyhai #C*ModelisationAvancee
Queues: solutions Bucketing & croissance monotone
10 000 éléments/bucket
#p:#bucket1 #t1 #t2 #t3 … #t10000
m1 m2 m3 … m10000
#p:#bucket2 #t100001 #t10002 #t10003 … #t20000
m100001 m10002 m10003 … m20000
#p:#bucket3 #t200001 #t20002 #t20003 … #t30000
m200001 m20002 m20003 … m30000
177
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Multiple sources sn
ordonner les sources: s1, s2 … sn
178
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Multiple sources sn
ordonner les sources: s1, s2 … sn round-robin sur les #bucket, pour sm : #bucket%n = m%n
179
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Multiple sources sn
ordonner les sources: s1, s2 … sn round-robin sur les #bucket, pour sm : #bucket%n = m%n bucket suivant si #élément = 10 000
180
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Exemple n = 3
s1 #bucket = {1, 4, 7…}, s2 #bucket = {2, 5, 8…}, s3 #bucket = {3, 6, 9…}
181
#p:#bucket1 … … … … … … … … … …
#p:#bucket2 … … … … … … … … … …
#p:#bucket3 … … … … … … … … … …
#p:#bucket4 … … … … … … … … … …
#p:#bucket5 … … … … … … … … … …
s1
s1
s2
s2
s3
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Multiple consommateurs cn
idem
182
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Rollback
pas de suppression !!! cm remet messages dans #bucketm+? futur → comptage faussé pour cm+?
183
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Rollback
pas de suppression !!! cm remet messages dans #bucketm+? futur → comptage faussé pour cm+?
184
#p:#bucketm … … … … … … … … … …
#p:#bucketm+1 … … … … … … … … … …
#p:#bucketm+2 … … … … … … … … … … … … … … … …
#p:#bucketm+3 … … … … … … … … … …
@doanduyhai #C*ModelisationAvancee
Queues: accès concurrents Solution rollback
compteur d’éléments supplémentaires pour messages rollbackés complexité technique
185
#p:#bucketm … … … … … … … … … …
#p:#bucketm+1 … … … … … … … … … …
#p:#bucketm+2 … … … … … … … … … … … … … … … …
#p:#bucketm+3 … … … … … … … … … …
+3
@doanduyhai #C*ModelisationAvancee
Détection de présence/d’absence
186
@doanduyhai #C*ModelisationAvancee
Détection de présence/d’absence Jetons de sécurité
Vérification
CREATE TABLE user_tokens ( user_id int, token uuid, PRIMARY KEY(user_id, token)) ;
SELECT count(*) FROM user_tokens WHERE user_id =… AND token =…
187
@doanduyhai #C*ModelisationAvancee
Détection de présence/d’absence Chemin de lecture
Bloom filter en mémoire partition key cache en mémoire accès disque SSTable
188
@doanduyhai #C*ModelisationAvancee
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache
Compression offset
Key Index Sample 4
5
Memtable
1 2
7
∑
3
SStable 1
Column Family User
6
Détection de présence/d’absence SELECT count(*) FROM user_tokens WHERE user_id =… AND token =…
189
@doanduyhai #C*ModelisationAvancee
Détection de présence/d’absence Nouvelle modélisation Nouveau chemin de lecture
Bloom filter en mémoire partition key cache en mémoire accès disque SSTable en cas de faux positif
CREATE TABLE user_token ( user_id int, token uuid, PRIMARY KEY((user_id, token))) ;
190
@doanduyhai #C*ModelisationAvancee
Mémoire Off-Heap
Disque
Row Cache
Mémoire JVM Heap
Bloom Filter
Partition Key Cache Memtable
1 2
4
∑
3
Détection de présence/d’absence SELECT count(*) FROM user_tokens WHERE user_id =… AND token =…
191
@doanduyhai #C*ModelisationAvancee
Row cache Use-cases
données référentielles taille raisonnable partitions accédées très fréquemment
192
@doanduyhai #C*ModelisationAvancee
Row cache Use-cases
données référentielles taille raisonnable partitions accédées très fréquemment
Important
monitoring nécessaire du hit rate & de la taille des données !!! coût de la sérialisation/dé-sérialisation (off-heap)
193
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
LES INDEX SECONDAIRES
Implémentation Use-cases recommandés
Retour d’expérience
@doanduyhai #C*ModelisationAvancee
Implémentation Naïve
CREATE TABLE user_par_pays ( pays text, user_id int, PRIMARY KEY(pays, user_id)) ;
196
@doanduyhai #C*ModelisationAvancee
Implémentation Naïve Problèmes
197 partitions max → wide rows distribution pays non uniforme → hot-spots m.a.j. non atomique bucketing non adapté
CREATE TABLE user_par_pays ( pays text, user_id int, PRIMARY KEY(pays, user_id)) ;
197
@doanduyhai #C*ModelisationAvancee
11:Martin, 85:Sophie, 57:Smith
Implémentation Distribuée
10, Dupond, FR 11, Martin, FR 85, Sophie, FR 25, John, US 74, Helen, US 57, Smith, UK 18, Heinrich, DE
n4
n3
n2
10:Dupond, 25:John,
18:Heinrich
n1
74:Helen
FR → {10} US → {25} DE → {18}
FR → {11,85} UK → {57}
US → {74}
198
@doanduyhai #C*ModelisationAvancee
11:Martin, 85:Sophie, 57:Smith
Implémentation Conséquences bénéfiques
meilleure distribution, hot-spots même distribution que données
n4
n3
n2
n1
10:Dupond, 25:John,
18:Heinrich 74:Helen
FR → {10} US → {25} DE → {18}
FR → {11,85} UK → {57}
US → {74}
199
@doanduyhai #C*ModelisationAvancee
11:Martin, 85:Sophie, 57:Smith
Implémentation Conséquences fâcheuses
lecture index → scan de N/R nœuds !!! N = #nœuds R = replication factor
n4
n3
n2
n1
10:Dupond, 25:John,
18:Heinrich 74:Helen
FR → {10} US → {25} DE → {18}
FR → {11,85} UK → {57}
US → {74}
200
N = 4,R = 2
@doanduyhai #C*ModelisationAvancee
11:Martin, 85:Sophie, 57:Smith
Implémentation Mais pourquoi ???
range scan pour lire les données …de toute façon
n4
n3
n2
n1
DE → {18}
US → {25,74}
FR → {10,11,85} UK → {57}
10:Dupond, 25:John,
18:Heinrich 74:Helen
201
@doanduyhai #C*ModelisationAvancee
Use-cases recommandés Bon candidats
peu de valeurs indexées # (mais assez quand même)
202
@doanduyhai #C*ModelisationAvancee
Use-cases recommandés Bon candidats
peu de valeurs indexées # (mais assez quand même)
Mauvais candidats
très peu de valeurs indexées # (index d’un booléen ) beaucoup de valeurs indexées # (index sur email ) m.a.j./suppression fréquentes très gros cluster
203
@doanduyhai #C*ModelisationAvancee
Retour d’expérience Queue pour état d’avancement
états : { ‘TODO’, ‘IN PROGRESS’, ‘PROCESSED’, ‘DONE’} colonne : item_id/état index secondaire sur état
204
@doanduyhai #C*ModelisationAvancee
Retour d’expérience Queue pour état d’avancement
états : { ‘TODO’, ‘IN PROGRESS’, ‘PROCESSED’} colonne : item_id/état index secondaire sur état
Transition
prendre 1000 items avec état ‘TODO’ m.a.j. de l’état en ‘IN PROGRESS’ traiter les items mettre les 1000 états en ‘PROCESSED’ si erreur, remettre état en ‘TODO’
205
@doanduyhai #C*ModelisationAvancee
Retour d’expérience Solution
utiliser JMS pour queue queue avec wide row, bucketing & accès concurrent compteur pour messages réinjectés enlever index secondaires
206
@doanduyhai #C*ModelisationAvancee
Q & R
@doanduyhai #C*ModelisationAvancee
<FUTUR> C*
Requêtes inter-clustering Colonnes statiques
User defined type
@doanduyhai #C*ModelisationAvancee
Requêtes inter-clustering (2.0.6)
Données
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, commentaire text, PRIMARY KEY(film_id, note, user_id));
Note User_id Commentaire 1 10 Bla bla bla … … … 1 49 Bla bla bla 1 82 Bla bla bla 2 13 Bla bla bla 2 54 Bla bla bla 3 15 Bla bla bla
11 notes de valeur 1
209
@doanduyhai #C*ModelisationAvancee
Requêtes inter-clustering (2.0.6) Pagination par 10
user_id 10 à 49
SELECT * FROM notes_film WHERE film_id=.. AND note=1 LIMIT 10
210
Note User_id Commentaire 1 10 Bla bla bla … … … 1 49 Bla bla bla 1 82 Bla bla bla 2 13 Bla bla bla 2 54 Bla bla bla 3 15 Bla bla bla
11 notes de valeur 1
@doanduyhai #C*ModelisationAvancee
Requêtes inter-clustering (2.0.6) Deuxième page
user_id 82
SELECT * FROM notes_film WHERE film_id=.. AND note=1 AND user_id>49 LIMIT 10
211
Note User_id Commentaire 1 10 Bla bla bla … … … 1 49 Bla bla bla 1 82 Bla bla bla 2 13 Bla bla bla 2 54 Bla bla bla 3 15 Bla bla bla
11 notes de valeur 1
@doanduyhai #C*ModelisationAvancee
Requêtes inter-clustering (2.0.6) Solution naïve (à la SQL)
SELECT * FROM notes_film WHERE film_id=.. AND note≥1 AND user_id>49 LIMIT 10
212
Note User_id Commentaire 1 10 Bla bla bla … … … 1 49 Bla bla bla 1 82 Bla bla bla 2 13 Bla bla bla 2 54 Bla bla bla 3 15 Bla bla bla
@doanduyhai #C*ModelisationAvancee
Requêtes inter-clustering (2.0.6) Nouvelle syntaxe
SELECT * FROM notes_film WHERE film_id=.. AND (note,user_id) > (1,49) LIMIT 10
213
Note User_id Commentaire 1 10 Bla bla bla … … … 1 49 Bla bla bla 1 82 Bla bla bla 2 13 Bla bla bla 2 54 Bla bla bla 3 15 Bla bla bla
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) CREATE TABLE notes_film (
film_id uuid, note uuid, user_id int, commentaire text, PRIMARY KEY(film_id, note, user_id));
CREATE TABLE films ( film_id uuid, titre text, synopsis text, acteurs_id set<int>, acteurs set<text> PRIMARY KEY(film_id));
214
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) Afficher le film et les notes
1 requête pour film 1 requête pour notes
215
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) Objectif: 1 seule requête Solution naïve
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, titre text, synopsis text, acteurs_id set<int>, acteurs set<text>, commentaire text, PRIMARY KEY(film_id, note, user_id));
216
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) RowKey: 98bc1cec-4c4b-468b-a20e-2af142a26c97 => (column=1:10:, value=, …) => (column=1:10:titre, value=…) => (column=1:10:synopsis, value=…) => (column=1:10:acteurs_ids, value=…) => (column=1:10:acteurs, value=…) => (column=1:10:commentaire, value=‘Quel navet! Passez votre chemin, vous …’, …) => (column=1:23:, value=, …) => (column=1:23:titre, value=…) => (column=1:23:synopsis, value=…) => (column=1:23:acteurs_ids, value=…) => (column=1:23:acteurs, value=…) => (column=1:23:commentaire, value=‘Un film à regarder avec le cerveau débranché’, …)
217
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) Avec colonnes statiques
CREATE TABLE notes_film ( film_id uuid, note uuid, user_id int, titre text static, synopsis text static, acteurs_id set<int> static, acteurs set<text> static, commentaire text, PRIMARY KEY(film_id, note, user_id));
218
@doanduyhai #C*ModelisationAvancee
Colonnes statiques (2.0.6) RowKey: 98bc1cec-4c4b-468b-a20e-2af142a26c97 => (column=:null:null:titre, value=…) => (column=:null:null:synopsis, value=…) => (column=:null:null:acteurs_ids, value=…) => (column=:null:null:acteurs, value=…) => (column=1:10:, value=, …) => (column=1:10:commentaire, value=‘Quel navet! Passez votre chemin, vous …’, …) => (column=1:23:, value=, …) => (column=1:23:commentaire, value=‘Un film à regarder avec le cerveau débranché’, …)
219
@doanduyhai #C*ModelisationAvancee
User defined type (2.1)
CREATE TABLE commentaires ( article_id int, date_creation timestamp, auteur user, contenu text, PRIMARY KEY (article_id, date_creation));
CREATE TYPE user ( id int, nom text, adresse text, bio text);
220
@doanduyhai #C*ModelisationAvancee
User defined type (2.1) Syntaxe INSERT INTO commentaires(article_id,date_creation,auteur,contenu) VALUES( 10, 2014-04-16 10:00:00+0100,
{ id: 156, nom: ‘Pierre DUPOND’, adresse: ‘12 rue de la Paix 75016 PARIS’, bio: ‘Je suis un habitant du 16ème…’ }, ‘Cet article a trop de fautes d’orthographe…’);
221
@doanduyhai #C*ModelisationAvancee
User defined type (2.1) Imbrication arbitraire
CREATE TYPE user ( id int, nom text, adresse location, bio text); CREATE TYPE location ( numero int, voie text, code_postal int, ville text);
222
@doanduyhai #C*ModelisationAvancee
User defined type (2.1) Limitations
modifications non atomiques lecture eager ALTER TYPE ADD, ALTER TYPE RENAME
223
@doanduyhai #C*ModelisationAvancee
User defined type (2.1) Sélection possible sur les champs internes UDT ≈ blob
SELECT auteur.nom, auteur.adresse, contenu FROM commentaires …
224
@doanduyhai #C*ModelisationAvancee
User defined type (2.1) Colonnes d’agrégation (clustering)
tri → ordre du type des champs déclarés Collections & Maps
unicité → comparaison des champs déclarés dans l’ordre
225
@doanduyhai #C*ModelisationAvancee
MERCI !
@doanduyhai doanduyhai@gmail.com doanduyhai
Recommended