.. _part1_chap2: *********************************************************************** Chapitre 2 : MapReduce *********************************************************************** **MapReduce** est un modèle de programmation pour traiter de **grands jeux de données en parallèle** sur un *cluster* de machines, en cachant au programmeur la complexité de la distribution (partitionnement, communication, tolérance aux pannes). C'est le socle du big data (Hadoop, Spark). Objectifs ========= À la fin de ce chapitre, vous devez pouvoir : - Décrire les trois temps de MapReduce : **Map → regroupement par clé → Reduce** - Modéliser un problème en *mappers* et *reducers* - Connaître des patrons classiques (word count, matrice-vecteur, opérations relationnelles) 1. Le modèle ============ Les données sont vues comme des **paires (clé, valeur)**. Le calcul se fait en trois temps : 1. **Map** : chaque *mapper* traite un fragment des données en entrée et émet des paires intermédiaires ``(clé, valeur)``. 2. **Regroupement par clé** (*shuffle & sort*) : le système regroupe toutes les valeurs partageant la **même clé** : ``(clé, [valeurs])``. *(Pris en charge automatiquement.)* 3. **Reduce** : chaque *reducer* reçoit une clé et la liste de ses valeurs, et émet le résultat agrégé. .. math:: \text{map}: (k_1, v_1) \rightarrow \text{list}(k_2, v_2) \qquad \text{reduce}: (k_2, \text{list}(v_2)) \rightarrow \text{list}(v_3) 2. Exemple canonique : *word count* =================================== Compter les occurrences de chaque mot dans une énorme collection de documents. .. code-block:: python def map(doc_id, texte): for mot in texte.split(): emit(mot, 1) # (mot, 1) def reduce(mot, comptes): # comptes = [1, 1, 1, ...] emit(mot, sum(comptes)) # (mot, total) **Déroulé** sur deux documents ``d1="le chat le"`` et ``d2="le chien"`` : - Map(d1) → ``(le,1) (chat,1) (le,1)`` ; Map(d2) → ``(le,1) (chien,1)`` - Regroupement → ``(le,[1,1,1]) (chat,[1]) (chien,[1])`` - Reduce → ``(le,3) (chat,1) (chien,1)`` .. tip:: Un **combiner** (mini-reduce local côté mapper) réduit le volume transféré : Map(d1) émettrait directement ``(le,2)(chat,1)``. Possible quand l'agrégation est **associative et commutative** (somme, max…). 3. Produit matrice-vecteur ========================== Calculer :math:`x_i = \sum_j m_{ij} v_j` (utile pour **PageRank**, :doc:`chap8 <../part2/chap8>`). - **Map** : pour chaque élément non nul :math:`m_{ij}`, émettre ``(i, m_ij * v_j)`` (le vecteur ``v`` est diffusé à tous les mappers). - **Reduce** : pour chaque ligne ``i``, sommer les valeurs reçues → ``(i, x_i)``. Si ``v`` ne tient pas en mémoire, on découpe ``M`` et ``v`` en **bandes**. 4. Opérations relationnelles ============================ MapReduce exprime aussi les opérations de l'algèbre relationnelle : .. list-table:: :header-rows: 1 :widths: 24 76 * - Opération - Idée MapReduce * - **Sélection** σ - Map émet les tuples satisfaisant la condition ; pas de reduce * - **Projection** π - Map émet les attributs gardés ; reduce déduplique * - **Jointure** R ⋈ S sur ``a`` - Map émet ``(a, (R, reste))`` et ``(a, (S, reste))`` ; reduce combine les tuples R×S de même clé ``a`` * - **Group-by / agrégation** - Map émet ``(clé_de_groupe, valeur)`` ; reduce agrège (somme, count…) 5. Exécution et tolérance aux pannes ==================================== Le *framework* gère : le découpage des entrées, l'ordonnancement des tâches sur les nœuds, le *shuffle & sort*, et la **reprise sur panne** (une tâche échouée est **ré-exécutée** ailleurs, car map/reduce sont des fonctions **sans état**). **Écosystème** : **Hadoop** (HDFS + MapReduce) puis **Spark** (calcul en mémoire, *RDD/DataFrame*, bien plus rapide pour les algorithmes itératifs comme K-means ou PageRank). Exercices ========= .. admonition:: Exercice 1 — Map/Reduce d'une moyenne :class: tip On a des relevés ``(ville, température)``. Écrivez les fonctions *map* et *reduce* qui calculent la **température moyenne par ville**. Attention au piège du combiner. .. raw:: html
▶ Voir la correction ``map(_, (ville, temp)) → emit(ville, (temp, 1))`` puis ``reduce(ville, liste) → emit(ville, somme(temps)/somme(comptes))``. **Piège** : la moyenne n'est **pas** associative ; un combiner naïf qui ferait la moyenne des moyennes serait faux. Il faut transporter des **paires (somme, compte)** et n'effectuer la division qu'au reduce final. .. raw:: html
.. admonition:: Exercice 2 — Jointure :class: tip Soit ``Ventes(produit, montant)`` et ``Produits(produit, catégorie)`` réparties sur plusieurs serveurs. Décrivez la jointure en MapReduce pour obtenir le montant total **par catégorie**. .. raw:: html
▶ Voir la correction **Job 1 (jointure)** : map émet ``(produit, ("V", montant))`` et ``(produit, ("P", catégorie))`` ; le reduce, pour chaque produit, associe la catégorie à chaque montant → ``(catégorie, montant)``. **Job 2 (agrégation)** : map relaye ``(catégorie, montant)`` ; reduce somme par catégorie. (On peut aussi fusionner en chaînant les deux étapes.) .. raw:: html