diff --git a/cesi/nosql/.gitignore b/cesi/nosql/.gitignore new file mode 100644 index 0000000..ac5e6cb --- /dev/null +++ b/cesi/nosql/.gitignore @@ -0,0 +1,2 @@ +*.pdf +Slides/*.html \ No newline at end of file diff --git a/cesi/nosql/01-Présentation.md b/cesi/nosql/01-Présentation.md new file mode 100644 index 0000000..8510b06 --- /dev/null +++ b/cesi/nosql/01-Présentation.md @@ -0,0 +1,233 @@ +# Initiation à NoSQL + +## Sylvain Eliade, Cadoles + +------ + +## C'est quoi NoSQL ? + +* Désigne toutes les bases de données non-relationnelles +* Base relationnelle (RDBMS) = généralement SQL +* Mais on peut faire du SQL non-relationnel ! +* Donc maintenant, NoSQL = "Not Only SQL" +* Au final, un mot un peu trop vague, comme "Web 2.0" + +--- + +## Rappel : RDBM + +
+ +
+ +--- + +## Rappel : ACID + +* Atomique = tout changement (transaction) à la BDD doit fonctionner ou échouer en entier (mais pas être exécuté à moitié et échouer à moitié !) +* Consistant = toutes les règles imposées doivent être valides (clés, triggers, contraintes, etc.) pour empêcher la corruption de la BDD +* Isolation = les transactions pouvant être exécutées en même temps, une requête concurrente ne doit pas avoir accès à un état transitoire de la BDD +* Durabilité = une transaction écrite dans la BDD doit le rester en cas de crash logiciel ou coupure du serveur + +--- + +## Différences entre RDBMS et NoSQL + +* Généralement pas ACID (Atomicity, Consistency, Isolation, Durability) +* Mais parfois oui (CouchDB), parfois partiellement +* Théoriquement plus facile à dimensionner horizontalement +* En pratique les RDBMS se dimensionnent très bien aussi +* La plus grosse différence et intérêt : non relationnel ! :) + +--- + +## Les familles de bases NoSQL + +* Clé-valeur (Berkeley DB, memcached, Redis, Apache Ignite, etc.) +* Document (MongoDB, Apache CouchDB, etc.) +* Colonnes ou "wide column" (Cassandra, HBase, etc.) +* Graph (Neo4j, Apache Giraph…) + +--- + +### Quand utiliser une BDD noSQL ? + +* Besoin de très grosses performances +* … ou stockage de très grosses données +* Données non adaptées à un RDBM +* Et surtout : pas de besoins relationnels ! + +--- + +### Quand utiliser une BDD noSQL ? + +* … mais même dans ces cas, un RDBM est souvent plus simple et rapide à mettre en œuvre +* Et un RDBM supporte aussi de très grandes quantités de données et de bonnes performances… si vous savez bien l'utiliser :) +* Vous n'êtes pas Google, Amazon ou Alibaba ! +* Une solution SQL (Postgre, MySQL, SQL Server, Oracle…) sera adaptée à 95% des besoins + +--- + +## Bases de données clé-valeur + +* Aussi vieilles que l'informatique ! +* On référence une valeur à l'aide d'une clé pour aider à retrouver la valeur +* La valeur ne fait pas de différence pour le moteur de BDD (opaque) +* On ne peut donc pas trouver une entrée juste avec une partie de la valeur +* Du plus simple (memcached, Berkeley DB) au plus évolué (Redis) +* Rapide, simple, efficace + +--- + +## Bases de données clé-valeur + +
+ +
+ +--- + +### Quand utiliser une BDD clé-valeur ? + +* Données simples et à une seule dimension +* Données qui peuvent expirer +* Pas forcément besoin de persistance +* Exemples : cache, queue, statistiques… + +--- + +### Quand ne pas utiliser une BDD clé-valeur ? + +* Données structurées sur plusieurs niveaux (2D+) +* Besoins de jointures entre les données +* Besoin d'index sur le contenu des valeurs +* Besoin de chercher dans les valeurs +* Besoin de lister toutes les clés (lent) +* Données très grandes (selon le moteur de BDD, par exemple si les données sont stockées en RAM) + +--- + +## Bases de données document + +* Évolution du modèle clé-valeur (document = valeur) +* Sauf que le contenu de la valeur peut être indexé / référencé +* Plutôt adapté à un modèle où les documents sont indépendants et n'ont pas de relation entre eux +* Exemples : MongoDB, CouchDB, etc. + +--- + +## Bases de données document + +
+ +
+ +--- + +### Quand utiliser une BDD document ? + +* Données structurées ou complexes +* Structure des données très variable +* Données non relationnelles +* Pas de schéma +* Exemple : fiches produit d'un site ecommerce + +--- + +## Base de données orientée colonnes + +* Comme une BDD clé-valeur, mais en 2D ! +* On retrouve une donnée à l'aide d'une clé (ligne) et d'un nom de colonne +* Toutes les lignes n'ont pas forcément les même colonnes (contrairement à une table dans un RDBM) +* Les colonnes sont donc indépendantes d'une ligne à l'autre +* Exemples : Cassandra, HBase (historiquement : Google BigTable) + +--- + +### Quand utiliser une BDD orientée colonnes ? + +* Beaucoup d'écritures (ajouts) +* Grand volume de données +* Besoins de réplication +* Données peu structurées +* Données en 2D uniquement (clé d'accès + colonnes) +* Exemple idéal : statistiques, messageries temps réel + +--- + +### Quand ne pas utiliser une BDD orientée colonnes ? + +* Beaucoup de mises à jour / suppression de lignes +* Besoin de transactions, de rollbacks, de triggers, joins, etc. +* Besoin ACID : ce n'est pas du tout fait pour +* Besoin de parcourir toutes les données stockées : lent ! + +--- + +## Base de données graph + +* Documents (= nœuds) liés ensemble par des relations (= arcs ou graphs) +* Les relations peuvent disposer de propriétés selon le contexte +* On peut accéder aux nœuds via leurs relations + +--- + +## Base de données graph + +
+ +
+ +--- + +### Quand utiliser une BDD graph ? + +* Données non structurées (pas de schéma) +* Liées ensemble de manière non ordonnée +* Exemples : graph d'amis (réseau social), recommandations de produits, détection de fraude… + +--- + +### Quand ne pas utiliser une BDD graph ? + +* Données non liées +* Données séquentielles +* Pas de besoin de lire les données +* Données dans un schéma fixe +* Besoin de stocker beaucoup de données dans les propriétés + +--- + +## NoSQL : une solution parmi d'autres + +* Les bases NoSQL ont été créées pour répondre à des besoins de très gros acteurs +* Besoins qui sont rarement atteints ailleurs +* NoSQL est une solution, pas LA solution +* Bien évaluer le problème avant de choisir la ou les solutions :) +* Le bon côté : NoSQL a initié une modernisation des RDBMs + +--- + +## Google abandonne NoSQL + +* Google, à l'origine de BigTable et MapReduce, premières solutions NoSQL +* … sont repassés à SQL avec [Spanner et le "Standard SQL"](https://blog.timescale.com/blog/why-sql-beating-nosql-what-this-means-for-future-of-data-time-series-database-348b777b847a/) commun à plusieurs outils internes. +* Car SQL facilite l'arrivée de nouveaux développeurs et la possibilité de communiquer avec des outils différents mais avec le même langage. + +--- + +## NoSQL dans SQL + +* Stockage JSON dans PostgreSQL (depuis la version 9.2), MySQL (5.7.8), SQLite (3.9.0), SQL Server +* Recherche plein texte, index sur les données JSON, jointures ! +* Implémentation légèrement différente dans SQLite +* Performance : pas forcément lent +* PostgreSQL est plus rapide avec JSONB que MongoDB par exemple, mais nécessite plus d'optimisation de la configuration + +--- + +## Dans quel cas utiliser une BDD NoSQL ? + +* Gros volume de données +* Données très spécifiques et non-relationnelles +* Petit projet, "pour tester", qui ne sera pas amené à évoluer, car changer de BDD est lourd ! \ No newline at end of file diff --git a/cesi/nosql/02-Redis.md b/cesi/nosql/02-Redis.md new file mode 100644 index 0000000..fac04ca --- /dev/null +++ b/cesi/nosql/02-Redis.md @@ -0,0 +1,46 @@ +# Introduction à Redis + +## Sylvain Eliade, Cadoles + +--- + +## Redis + +* Stockage en mémoire +* Mais pas que… Stockage persistant optionnel +* Base de données, cache, pub-sub +* Réplication, transactions, partitionnement… +* Nombreux types de structures de données, majoritairement clé-valeur +* Très très rapide +* Un seul thread, mais c'est pas grave, c'est pas le CPU qui limite +* Scriptable avec Lua + +--- + +## Types de structures de données + +* Chaînes de caractère +* Listes simples +* Jeux de données uniques non-ordonnées (sets) +* Dictionnaires (hashes) +* Liste triée (sorted set) + +--- + +## Le protocole de Redis + +* Protocole texte simple (utilisable par telnet !) +* Commandes texte suivies d'arguments : `SET clé valeur` +* Réponse texte : `OK`, chaînes, etc. + +--- + +## Commandes de base + +* [Commandes Redis](https://redis.io/commands) +* [Types Redis](https://redis.io/topics/data-types) +* Redis-cli ! +* `SET test coucou`… `OK` +* `GET test`… `"coucou"` +* `DEL test`… `(integer) 1` = une clé supprimée, c'est bon ! +* `KEYS *` = lister toutes les clés diff --git a/cesi/nosql/03-Exercices_Redis.md b/cesi/nosql/03-Exercices_Redis.md new file mode 100644 index 0000000..8e700d0 --- /dev/null +++ b/cesi/nosql/03-Exercices_Redis.md @@ -0,0 +1,85 @@ +# Exercices Redis + +Utiliser redis-cli et copier/coller le résultat de chaque exercice (commandes et retours de Redis) dans un fichier texte. + +--- + +## Exercice 1 + +* créer une clé "normale" avec sa valeur +* récupérer le résultat +* supprimer la clé + +--- + +## Exercice 2 : expiration + +* créer une clé +* lui donner une expiration courte (quelques secondes) +* vérifier que la clé "disparaît" toute seule + +--- + +## Exercice 3 : compteurs + +* Créer une clé contenant un numéro +* L'incrémenter et le décrementer avec les commandes Redis adaptées + +--- + +## Exercice 4 : liste unique non ordonnée + +* Créer une liste unique non ordonnée (set, `SADD`) +* Y insérer plusieurs entrées +* Insérer des entrées en double +* Vérifier que la liste ne contient pas de doublons +* Supprimer des entrées + +--- + +## Exercice 5 : liste ordonnée par ordre d'insertion + +* Créer une liste ordonnée (`LPUSH`) +* Ajouter des entrées par la gauche et la droite (`LPUSH` et `RPUSH`) +* Récupérer une partie de la liste +* Supprimer des éléments par la gauche, la droite (pop) et arbitrairement (rem) + +--- + +## Exercice 6 : hashes + +* Créer un hash avec plusieurs sous-clés et sous-valeurs (`HSET` et `HMSET`) +* Récupérer toutes les sous-valeurs du hash +* Modifier une seule sous-valeur +* Récupérer une seule sous-valeur + +--- + +## Exercice 7 : liste ordonnée par score + +* Enregistrer les scores de 10 joueurs de Tetris dans une liste ordonnée (`ZADD`), avec leurs noms et leurs scores +* Récupérer la liste des 5 meilleurs joueurs (`ZRANGE`) +* Récupérer le score du joueur Bob + +--- + +## Mini-projet : newsfeed de réseau social + +Note : tout le monde doit utiliser le même serveur Redis au final ! + +Travail en équipe (groupes et classe). + +Prérequis avant de coder : se mettre d'accord avec le reste de la classe de la structure des données à stocker dans Redis (types, noms des clés, etc.) ! + +Pour rendre les choses plus faciles : la base Redis contiendra une liste unique non ordonnée "utilisateurs" contenant la liste des noms/pseudo des utilisateurs du réseau social. + +* L'application doit permettre d'ajouter des posts à sa propre timeline +* Les posts doivent contenir : date, nom de l'utilisateur, contenu du post +* Chaque utilisateur a une timeline différente selon qui il veut suivre +* Mais le contenu des posts ne doit être stocké qu'une seule fois ! +* L'application doit propager ces posts aux utilisateurs inscrits à sa propre timeline +* L'application doit permettre de lire les derniers posts des utilisateurs auxquels on s'est inscrit + +Note : l'implémentation de la gestion des utilisateurs et de l'inscription aux timelines des autres utilisateurs est facultative (création d'utilisateur et inscription aux timelines sont possibles à la main avec redis-cli). + +Note 2 : certaines données associatives peuvent être doublonnées (présence dans plusieurs clés) si besoin. \ No newline at end of file diff --git a/cesi/nosql/Fiche de preparation RIL BIG DATA B1- NoSQL.docx b/cesi/nosql/Fiche de preparation RIL BIG DATA B1- NoSQL.docx new file mode 100644 index 0000000..e5462e4 Binary files /dev/null and b/cesi/nosql/Fiche de preparation RIL BIG DATA B1- NoSQL.docx differ diff --git a/cesi/nosql/Images/document.png b/cesi/nosql/Images/document.png new file mode 100644 index 0000000..3fcdc4a Binary files /dev/null and b/cesi/nosql/Images/document.png differ diff --git a/cesi/nosql/Images/graph.png b/cesi/nosql/Images/graph.png new file mode 100644 index 0000000..50f78f3 Binary files /dev/null and b/cesi/nosql/Images/graph.png differ diff --git a/cesi/nosql/Images/kv.png b/cesi/nosql/Images/kv.png new file mode 100644 index 0000000..8604893 Binary files /dev/null and b/cesi/nosql/Images/kv.png differ diff --git a/cesi/nosql/Images/logo.svg b/cesi/nosql/Images/logo.svg new file mode 100644 index 0000000..1a30013 --- /dev/null +++ b/cesi/nosql/Images/logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cesi/nosql/Images/rdbm.svg b/cesi/nosql/Images/rdbm.svg new file mode 100644 index 0000000..60be83e --- /dev/null +++ b/cesi/nosql/Images/rdbm.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + product + + id + + name + + seo_name + + slug + + reference + + short_description + + seo_description + + position + + weight + + created + + updated + + long_description + + details + + ingredients + + status + + tva + + label + + description_comp + + product_categories + + product_id + + category_id + + product_price + + id + + product_id + + price + + context_id + + price_ttc + diff --git a/cesi/nosql/Makefile b/cesi/nosql/Makefile new file mode 100644 index 0000000..b0b163e --- /dev/null +++ b/cesi/nosql/Makefile @@ -0,0 +1,14 @@ +.PHONY: all slides +.ONESHELL: + +all: pdf + +slides: + for i in *.md; do php Slides/make.php "$$i" > "Slides/$${i##.md}.html"; done; + +pdf: slides + cd Slides; for i in *.html; do prince -o "../$${i##.html}.pdf" "$$i"; done; + +clean: + rm -f Slides/*.html + rm -f *.pdf \ No newline at end of file diff --git a/cesi/nosql/Slides/Images b/cesi/nosql/Slides/Images new file mode 120000 index 0000000..378ca6f --- /dev/null +++ b/cesi/nosql/Slides/Images @@ -0,0 +1 @@ +../Images \ No newline at end of file diff --git a/cesi/nosql/Slides/make.php b/cesi/nosql/Slides/make.php new file mode 100644 index 0000000..99bdac5 --- /dev/null +++ b/cesi/nosql/Slides/make.php @@ -0,0 +1,99 @@ + DESTINATION_HTML\n"); +} + +$lines = file($argv[1]); +$out = ''; +$in_list = $in_section = false; +$section_class = null; + +function escape($str) { + $str = htmlspecialchars($str); + $str = str_replace('<!--', '', $str); + $str = str_replace('-->', '', $str); + + $str = preg_replace_callback('/`(.*?)`/', function ($match) { + return '' . $match[1] . ''; + }, $str); + + $str = preg_replace_callback('/\[(.*?)\]\((.*?)\)/', function ($match) { + return sprintf('%s', $match[2], $match[1]); + }, $str); + + return $str; +} + +foreach ($lines as $line) +{ + if (preg_match('/^---+(?:\s*=(\w+))?$/', $line, $match)) { + if ($in_section) { + if ($in_list) { + $out .= ' ' . PHP_EOL; + $in_list = false; + } + + $out .= '' . PHP_EOL . PHP_EOL; + $in_section = false; + } + + $section_class = !empty($match[1]) ? $match[1] : null; + + continue; + } + + if ('' === trim($line)) { + continue; + } + + if (!$in_section) { + $out .= sprintf('
', $section_class) . PHP_EOL; + $in_section = true; + } + + if (preg_match('/^(#+)\s+/', $line, $match)) { + $size = strlen($match[1]); + $title = trim(substr($line, strlen($match[0]))); + $out .= sprintf(' %s', $size, escape($title), $size) . PHP_EOL; + } + elseif (preg_match('/^\*\s+/', $line)) { + if (!$in_list) { + $out .= ' ' . PHP_EOL; + $in_list = false; + } + + $out .= '
' . PHP_EOL . PHP_EOL; + $in_section = false; +} + +?> + + + + + Slides + + + + + + + + + + + \ No newline at end of file diff --git a/cesi/nosql/Slides/print.css b/cesi/nosql/Slides/print.css new file mode 100644 index 0000000..f5fd1f3 --- /dev/null +++ b/cesi/nosql/Slides/print.css @@ -0,0 +1,12 @@ +body { + color: #666; + font-size: .8em; +} + +sup { + vertical-align: baseline; + font-size: 1.2em; + display: block; + font-style: italic; + color: #000; +} \ No newline at end of file diff --git a/cesi/nosql/Slides/slides.js b/cesi/nosql/Slides/slides.js new file mode 100644 index 0000000..1ded98b --- /dev/null +++ b/cesi/nosql/Slides/slides.js @@ -0,0 +1,148 @@ +var slides; +var current_slide = 0; +var current_slide_items; +var current_item = 0; +var slide_window; +var clock; +var timer; +var timer_interval; +var slideCounter; +var planned_duration = 30; + +function changeSlide(i, hide_items) { + if (current_slide + i < 0 || current_slide + i >= slides.length) + { + return true; + } + + if (slide_window) + { + slide_window.window.changeSlide(i, hide_items); + } + + slides[current_slide].classList.remove('current'); + + current_slide += i; + + // Change URL + window.location.hash = "#" + current_slide; + + // Get all items in a list + current_slide_items = slides[current_slide].getElementsByTagName('li'); + current_item = 0; + + for (var i = 0; i < current_slide_items.length; i++) + { + if (hide_items) + { + current_slide_items[i].classList.remove('shown'); + } + else + { + current_slide_items[i].classList.add('shown'); + } + } + + slides[current_slide].classList.add('current'); + slideCounter.innerHTML = current_slide + 1; + + return true; +} + +function progressSlide() { + if (current_item + 1 > current_slide_items.length || current_slide_items[current_item].classList.contains('shown')) + { + return changeSlide(1, true); + } + + current_slide_items[current_item++].classList.add('shown'); + + if (slide_window) + { + slide_window.window.progressSlide(); + } + + return true; +} + +function addTime() { + if (!clock) + { + clock = document.createElement('div'); + clock.className = 'clock'; + document.body.appendChild(clock); + } + + function zerofill(v) { + return ("0" + v).slice(-2); + } + timer = 0; + window.clearInterval(timer_interval); + timer_interval = window.setInterval(function () { + timer += 1; + clock.style.width = Math.round(timer / (planned_duration * 60) * 100) + '%'; + }, 1000); +} + +function initSlides () { + slides = document.getElementsByTagName('section'); + var current = window.location.hash.substr(1); + + if (current > 0) + { + current_slide = parseInt(current, 10); + } + + slides[current_slide].classList.add('current'); + current_slide_items = slides[current_slide].getElementsByTagName('li'); + + slideCounter = document.createElement('div'); + slideCounter.className = 'counter'; + document.body.appendChild(slideCounter); + + document.onkeydown = function (e) { + var prevent = false; + + if (e.key == " ") + { + prevent = progressSlide(); + } + else if (e.key == "ArrowRight" || e.key == "ArrowDown" || e.key == "PageDown") + { + prevent = changeSlide(1, false); + } + else if (e.key == "ArrowLeft" || e.key == "ArrowUp" || e.key == "PageUp") + { + prevent = changeSlide(-1, false); + } + else if (e.key == "Home") + { + prevent = changeSlide(-(current_slide), false); + } + else if (e.key == "End") + { + prevent = changeSlide(slides.length - 1, false); + } + else if (e.key == "o") + { + slide_window = window.open(document.location.href); + document.body.className = "notes"; + prevent = true; + } + else if (e.key == "s") + { + addTime(); + } + + if (prevent) + { + e.preventDefault(); + return false; + } + + return true; + }; +} + + +window.onload = initSlides; diff --git a/cesi/nosql/Slides/style.css b/cesi/nosql/Slides/style.css new file mode 100644 index 0000000..77b69f1 --- /dev/null +++ b/cesi/nosql/Slides/style.css @@ -0,0 +1,158 @@ +html, body { + height: 100%; +} + +body, section, h1, h2, h3, h4, h5, h6, p, ol, ul, li { + margin: 0; + padding: 0; +} + +h1 { + font-size: 400%; +} + +h2 { + font-size: 250%; + font-weight: normal; +} + +h3 { + font-size: 200%; + font-weight: normal; +} + +h1, h2, h3 { + margin-bottom: 1rem; +} + +ul, ol { + margin-left: 2em; + font-size: 200%; +} + +li { + margin: 1rem 0; +} + +ul ul, ul ol, ol ul, ol ol { + font-size: 100%; +} + +body { + background: #000; + font-family: "Eras Medium ITC", sans-serif; + overflow: hidden; + color: #fff; + font-size: 16pt; +} + +section { + width: 90%; + opacity: 0; + transition: all .7s ease-in-out; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + padding: 5%; + background: #000; + display: flex; + flex-direction: column; + justify-content: space-around; + text-shadow: 0px 0px 10px #000; + visibility: hidden; +} + +section.current { + transform: translate(0); + opacity: 1; + visibility: visible; +} + +a { + color: inherit; + cursor: pointer; + text-decoration: none; + border-bottom: 2px dashed darkblue; + line-height: .8em; + display: inline-block; + transition: all .5s; + border-radius: .2em; + padding: 0 .1em; +} + +a:hover { + border-color: #fff; + background: darkblue; +} + +.companyLogo { + margin-top: 3rem; +} + +section { + background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.7) 70%), url("Images/logo.svg") no-repeat center bottom; + background-size: contain; +} + +li { + transform: translateX(-500px); + opacity: 0; + transition: all .5s; +} + +li.shown { + transform: translate(0); + opacity: 1; +} + +sup { + display: none; +} + +.notes sup { + display: block; + background: #fff; + padding: .2em; + color: #000; +} + +.notes { + font-size: 12pt; +} + +.clock { + position: absolute; + bottom: 0px; + left: 0px; + right: 0px; + font-size: 2em; + background: #333; + height: 5px; + width: 0px; + transition: width 1s linear; +} + +.counter { + position: absolute; + right: 5px; + bottom: 5px; + font-size: 1.5em; +} + +figure { + margin: 0; + border-radius: .5em; + padding: 1em; + background: rgba(255, 255, 255, 0.3); + text-align: center; + height: 70%; +} + +figure img { + height: 95%; + background: #fff; + padding: .5em; + border-radius: .5em; +} \ No newline at end of file