Apache Pinot
appliqué avec
Spring Boot
OLTP OLAP Data Warehouse ETL Docker
Théorie, modélisation, pratique et projet complet
Auteur
Dr. BADR EL KHALYLY
Table des matières
1 Introduction à Apache Pinot 4
1.1 Contexte : l'explosion des données analytiques . . . . . . . . . . . . . . . . . . . . . 4
1.2 Qu'est-ce qu'Apache Pinot ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Historique et évolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Positionnement d'Apache Pinot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Caractéristiques clés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.6 Cas d'usage industriels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.7 Comparaison avec les alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.8 Écosystème Apache Pinot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2 OLTP vs OLAP : comparaison approfondie 7
2.1 Dénitions fondamentales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Architecture OLTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Architecture OLAP avec Pinot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Tableau comparatif OLTP vs OLAP . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5 Modélisation dimensionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3 Schéma relationnel OLTP 10
3.1 Principes de la modélisation relationnelle . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 Modèle métier : e-commerce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3 Schéma relationnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4 Script DDL Postgres complet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.5 Contraintes d'intégrité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4 Data Warehouse et schéma en étoile 13
4.1 Pourquoi un Data Warehouse ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Schéma en étoile (Star Schema) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.3 Schéma DW en étoile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.4 Table de faits : détail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.5 Typologie des dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.6 SCD : Slowly Changing Dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.7 Schéma en ocon vs étoile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.8 Schéma en ocon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
5 Conception ETL et mapping 17
5.1 Diagramme du pipeline ETL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.2 Extract : les sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.3 Transform : les règles de transformation . . . . . . . . . . . . . . . . . . . . . . . . 17
5.4 Load : les stratégies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.5 Table de mapping ETL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.6 Orchestration du pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
6 Architecture interne d'Apache Pinot 19
6.1 Les composants du cluster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6.2 Segments : unité de stockage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6.3 Tables OFFLINE, REALTIME, HYBRID . . . . . . . . . . . . . . . . . . . . . . . 19
6.4 Tenants et isolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6.5 Cycle de vie d'un segment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
6.6 Flux d'une requête SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
7 Indexation avancée 21
7.1 Les types d'index Pinot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.1 Forward Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.2 Dictionary Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.3 Inverted Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.4 Sorted Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.5 Range Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.6 Bloom Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.1.7 Star-Tree Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.2 Choisir le bon index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
7.3 Star-Tree en détail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
7.4 Exemple de conguration d'index . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
8 Ingestion batch et temps réel 23
8.1 Ingestion batch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
8.2 Ingestion temps réel via Kafka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
8.3 Ingestion hybride . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
8.4 Upsert et déduplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
9 Intégration Apache Pinot avec Spring Boot 25
9.1 Architecture applicative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
9.2 Dépendances Maven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
9.3 Entités JPA côté OLTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
9.4 Service Pinot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
9.5 Controllers REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9.6 Conguration : [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
10 Docker Compose et déploiement 28
10.1 Stack de déploiement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
10.2 Fichier [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
10.3 Commandes utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
11 Scripts OLTP et OLAP complets 30
11.1 Script OLTP (Postgres) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
11.2 Jeu de données de démonstration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
11.3 Schéma Pinot (OLAP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
11.4 Table Pinot (OLAP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
11.5 Requêtes analytiques typiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
12 Performance et observabilité 33
12.1 Métriques clés à surveiller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
12.2 Intégration Prometheus / Grafana . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
12.3 Optimisation des requêtes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
12.4 Dashboard Grafana recommandé . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
12.5 Tuning des serveurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Cours OLAP temps réel 2
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
13 Sécurité 35
13.1 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
13.2 Autorisation au niveau table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
13.3 Chirement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
13.4 Bonnes pratiques côté Spring Boot . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
14 Architecture Kafka + Pinot 36
14.1 Pourquoi Kafka avec Pinot ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
14.2 Topologie de bout en bout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
14.3 Conguration d'un producteur Spring Boot vers Kafka . . . . . . . . . . . . . . . . 36
14.4 Conguration Pinot REALTIME pour Kafka . . . . . . . . . . . . . . . . . . . . . 37
14.5 Débits et sizing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
14.6 Tolérance aux pannes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
15 Observabilité et monitoring avancés 38
15.1 La pyramide de l'observabilité . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
15.2 Logs structurés côté Spring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
15.3 Tracing distribué . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
15.4 Alerting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
16 Cas d'étude et exercices 40
16.1 Cas d'étude : tableau de bord e-commerce . . . . . . . . . . . . . . . . . . . . . . . 40
16.2 TP 1 : mise en place de la stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
16.3 TP 2 : ingestion des données dans Pinot . . . . . . . . . . . . . . . . . . . . . . . . 40
16.4 TP 3 : API d'analytics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
16.5 TP 4 : ingestion temps réel Kafka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
16.6 Questions de révision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
16.7 Éléments de correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
17 Multi-Stage Query Engine 43
17.1 Limites du moteur historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
17.2 Le Multi-Stage Engine (v2+) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
17.3 Activer le Multi-Stage Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
17.4 Flux d'exécution multi-stage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
17.5 Cas d'usage du Multi-Stage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
18 Patterns d'architecture avec Pinot 45
18.1 Pattern 1 : Lambda Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
18.2 Pattern 2 : CQRS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
18.3 Pattern 3 : Event Sourcing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
18.4 Pattern 4 : Funnel Analytics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
19 Conclusion 47
19.1 Pour aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
19.2 Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
20 Glossaire 48
21 Bibliographie et ressources 50
21.1 Livres de référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
21.2 Documentation ocielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
21.3 Articles et conférences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
21.4 Communauté . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Cours OLAP temps réel 3
Chapitre 1
Introduction à Apache Pinot
1.1 Contexte : l'explosion des données analytiques
Depuis deux décennies, le paysage informatique a été marqué par une croissance exponentielle
des volumes de données : logs applicatifs, événements métier, capteurs IoT, interactions utilisateurs.
Les entreprises ne cherchent plus seulement à stocker ces données mais à les exploiter en temps réel
pour prendre des décisions quasi immédiates.
Les bases relationnelles classiques (Postgres, MySQL, Oracle) sont excellentes pour les trai-
tements transactionnels (OLTP), mais deviennent vite un goulot d'étranglement lorsqu'il s'agit
d'exécuter des requêtes analytiques de type agrégation multi-critères sur des milliards de lignes
en quelques millisecondes.
C'est précisément dans ce contexte qu'émergent les bases OLAP temps réel comme Apache
Pinot, Apache Druid ou ClickHouse.
1.2 Qu'est-ce qu'Apache Pinot ?
Dénition
Apache Pinot est une base de données OLAP distribuée conçue pour orir des requêtes
analytiques à faible latence (millisecondes) sur de gros volumes de données, en temps réel
et en batch.
Apache Pinot a été créé originellement par LinkedIn en 2013, puis donné à la fondation Apache
en 2018. Il est aujourd'hui utilisé par des entreprises telles que Uber, Walmart, Stripe et Slack
pour alimenter leurs tableaux de bord utilisateurs et internes en temps réel. Pinot a été conçu
dès le départ pour supporter des workloads critiques où la latence p99 doit rester inférieure à 100
millisecondes, même avec des milliards de lignes ingérées quotidiennement.
1.3 Historique et évolution
2013 : projet interne chez LinkedIn pour alimenter le tableau de bord Who Viewed My
Prole .
2015 : open source sous licence Apache 2.0.
2018 : donation à la fondation Apache, incubation.
2021 : Pinot devient un Top-Level Project de la fondation Apache.
2022 : sortie de la version 0.10 avec le Multi-Stage Query Engine supportant les jointures.
20232024 : montée en puissance de l'intégration cloud (Kubernetes, déploiement managé
via StarTree).
4
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
1.4 Positionnement d'Apache Pinot
Ingestion Stockage Indexation Requêtes
temps réel colonnaire multiple SQL
1.5 Caractéristiques clés
Stockage colonnaire : optimisation pour les agrégations et les scans.
Indexation avancée : Inverted, Sorted, Range, Bloom, Star-Tree.
Ingestion hybride : batch (HDFS, S3) et temps réel (Kafka, Pulsar, Kinesis).
Haute disponibilité : architecture distribuée avec Zookeeper.
Requêtes SQL : compatibilité par le biais de Pinot-QL.
Scalabilité linéaire : ajout de serveurs sans interruption de service.
Multi-tenant : isolation logique entre équipes ou clients.
1.6 Cas d'usage industriels
Entreprise Cas d'usage Volumétrie
LinkedIn Prol, pubs, feed > 250 Mds événements/jour
Uber UberEats analytics temps réel > 10 Go/sec d'ingestion
Stripe Tableaux de bord marchands Milliards de transactions
Walmart Analytique e-commerce Scaling saisonnier Black Friday
Slack Analytique canaux et utilisateurs Requêtes p99 < 100 ms
Table 1.1 Exemples d'entreprises utilisant Apache Pinot en production
1.7 Comparaison avec les alternatives
Critère Apache Pinot Apache Druid ClickHouse
Latence Très faible Faible Faible
Ingestion temps réel Excellente (Kafka) Excellente Bonne
Support SQL Pinot-QL + multi-stage SQL Complet
Joins distribuées Oui (v1.0+) Limité Oui
Star-Tree index Oui (unique) Non Non
Écosystème Apache, StarTree Apache, Imply Yandex, Altinity
Table 1.2 Comparaison des moteurs OLAP temps réel
Astuce pratique
Pinot n'est pas un remplaçant d'une base OLTP. Il est complémentaire : Postgres gère les
transactions, Pinot sert les tableaux de bord analytiques.
Cours OLAP temps réel 5
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
1.8 Écosystème Apache Pinot
Apache Pinot s'inscrit dans un écosystème riche autour du traitement analytique :
Apache Kafka : bus de messages pour l'ingestion temps réel.
Apache Zookeeper : coordination du cluster.
Apache Helix : gestion des états du cluster.
Superset / Grafana : visualisation des résultats.
Trino / Presto : moteur SQL fédéré compatible Pinot.
Cours OLAP temps réel 6
Chapitre 2
OLTP vs OLAP : comparaison approfon-
die
2.1 Dénitions fondamentales
OLTP (Online Transaction Processing)
Système orienté transactions opérationnelles : INSERT, UPDATE, DELETE. Données nor-
malisées, très grande volumétrie d'opérations courtes.
OLAP (Online Analytical Processing)
Système orienté analyse : requêtes d'agrégation, tableaux de bord, statistiques. Données
dénormalisées (schéma en étoile ou ocon).
Ces deux paradigmes ont été formalisés par E. F. Codd dans les années 1990 pour distinguer
deux natures radicalement diérentes de traitement sur les données. L'OLTP vise la cohérence
transactionnelle (ACID), tandis que l'OLAP vise la performance sur les requêtes analytiques.
2.2 Architecture OLTP
Système OLTP transactions courtes
U1
Spring Boot Service Postgres
U2
Controller métier OLTP
CRUD JDBC / JPA
U3
Figure 2.1 Architecture d'un système OLTP avec Spring Boot et Postgres
7
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
2.3 Architecture OLAP avec Pinot
Cluster Apache Pinot
Postgres
OLTP Pinot
Controller Spring Boot
API
Pinot
Kafka ETL Broker
stream mapping
Pinot Dashboard
Server UI
Fichiers
CSV / S3 Zookeeper
Figure 2.2 Architecture OLAP temps réel avec Apache Pinot
2.4 Tableau comparatif OLTP vs OLAP
Critère OLTP OLAP
Objectif Opérations métier Analyse et reporting
Opérations INSERT / UPDATE / DELETE SELECT avec agrégations
Schéma Normalisé (3NF) Dénormalisé (étoile)
Volume Go To / Po
Latence écriture Millisecondes (tx) Diérée (ETL) ou temps réel
Latence lecture Millisecondes Millisecondes (Pinot)
Outil exemple Postgres, MySQL Apache Pinot, ClickHouse
Indexation B-Tree, Hash Inverted, Star-Tree
Cohérence ACID forte Eventuelle (BASE)
Utilisateurs Opérationnels Analystes, dirigeants
Table 2.1 Comparaison OLTP / OLAP
2.5 Modélisation dimensionnelle
L'approche dimensionnelle popularisée par Ralph Kimball repose sur deux concepts fonda-
mentaux :
Les tables de faits contiennent les mesures quantitatives (CA, quantités, durée) et les clés
étrangères vers les dimensions.
Les tables de dimensions décrivent le contexte (qui, quoi, où, quand, comment).
Cette approche s'oppose à celle de Bill Inmon qui préconise un entrepôt central normalisé
(3NF) alimentant des data marts dimensionnels.
Cours OLAP temps réel 8
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Astuce pratique
Pinot n'impose pas de schéma dimensionnel stricto sensu, mais il s'en inspire fortement : on
charge typiquement dans Pinot le résultat d'une jointure faits + dimensions pré-calculée,
sous forme d'une grande table aplatie.
Cours OLAP temps réel 9
Chapitre 3
Schéma relationnel OLTP
3.1 Principes de la modélisation relationnelle
Le modèle relationnel de Codd (1970) repose sur les principes suivants :
Les données sont organisées en relations (tables).
Chaque ligne est identiée de manière unique par une clé primaire.
Les relations entre tables sont matérialisées par des clés étrangères.
La normalisation (1NF, 2NF, 3NF, BCNF) élimine la redondance et les anomalies.
3.2 Modèle métier : e-commerce
Notre cas d'étude est une boutique en ligne vendant des produits. Les entités métier sont :
Customer : client ayant créé un compte.
Address : adresse de livraison d'un client (plusieurs possibles).
Category : catégorie produit.
Product : article vendu.
Order : commande passée par un client.
OrderItem : ligne de commande (produit + quantité).
3.3 Schéma relationnel
Le schéma ci-dessous présente la structure en 3NF sans chevauchement. Les èches représentent
les clés étrangères (FK).
10
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
CUSTOMERS CATEGORIES
PK id : BIGINT PK id : BIGINT
name : VARCHAR(100) name : VARCHAR(80)
email : VARCHAR(150) description : TEXT
country : VAR-
CHAR(50)
1..N
created_at : TIMES-
TAMP
PRODUCTS
1..N
PK id : BIGINT
ADDRESSES name : VARCHAR(100)
PK id : BIGINT ORDER_ITEMS
FK category_id
FK customer_id PK id : BIGINT price : NUMERIC(10,2)
street : VARCHAR(150) FK order_id
stock : INT
1..N FK product_id
city : VARCHAR(80) 1..N
zip : VARCHAR(10) quantity : INT
unit_price : NUME-
RIC(10,2)
1..N
ORDERS
PK id : BIGINT
FK customer_id
status : INT
total : NUMERIC(10,2)
created_at : TIMES-
TAMP
Figure 3.1 Schéma relationnel OLTP en 3NF
3.4 Script DDL Postgres complet
1 CREATE TABLE customers (
2 id BIGSERIAL PRIMARY KEY ,
3 name VARCHAR (100) NOT NULL ,
4 email VARCHAR (150) UNIQUE ,
5 country VARCHAR (50) ,
6 created_at TIMESTAMP DEFAULT NOW ()
7 );
8
9 CREATE TABLE addresses (
10 id BIGSERIAL PRIMARY KEY ,
11 customer_id BIGINT REFERENCES customers ( id ) ON DELETE CASCADE ,
12 street VARCHAR (150) ,
13 city VARCHAR (80) ,
14 zip VARCHAR (10)
15 );
16
17 CREATE TABLE categories (
18 id BIGSERIAL PRIMARY KEY ,
19 name VARCHAR (80) UNIQUE NOT NULL ,
20 description TEXT
21 );
22
23 CREATE TABLE products (
Cours OLAP temps réel 11
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
24 id BIGSERIAL PRIMARY KEY ,
25 name VARCHAR (100) NOT NULL ,
26 category_id BIGINT REFERENCES categories ( id ) ,
27 price NUMERIC (10 ,2) CHECK ( price >= 0) ,
28 stock INT DEFAULT 0
29 );
30
31 CREATE TABLE orders (
32 id BIGSERIAL PRIMARY KEY ,
33 customer_id BIGINT REFERENCES customers ( id ) ,
34 status INT DEFAULT 0 ,
35 total NUMERIC (10 ,2) ,
36 created_at TIMESTAMP DEFAULT NOW ()
37 );
38
39 CREATE TABLE order_items (
40 id BIGSERIAL PRIMARY KEY ,
41 order_id BIGINT REFERENCES orders ( id ) ON DELETE CASCADE ,
42 product_id BIGINT REFERENCES products ( id ) ,
43 quantity INT CHECK ( quantity > 0) ,
44 unit_price NUMERIC (10 ,2)
45 );
46
47 -- Index OLTP pour la performance
48 CREATE INDEX idx_orders_customer ON orders ( customer_id );
49 CREATE INDEX idx_orders_created ON orders ( created_at ) ;
50 CREATE INDEX idx_items_order ON order_items ( order_id ) ;
51 CREATE INDEX idx_items_product ON order_items ( product_id ) ;
Listing 3.1 Script DDL complet OLTP
3.5 Contraintes d'intégrité
Intégrité d'entité : PRIMARY KEY sur chaque table.
Intégrité référentielle : FOREIGN KEY sur customer_id, product_id, etc.
Intégrité de domaine : CHECK (price >= 0), CHECK (quantity > 0).
Unicité : UNIQUE(email), UNIQUE([Link]).
Attention
Dans un contexte analytique Pinot, ces contraintes ne seront plus appliquées : la cible OLAP
est dénormalisée et les erreurs doivent être détectées en amont (ETL ou source OLTP).
Cours OLAP temps réel 12
Chapitre 4
Data Warehouse et schéma en étoile
4.1 Pourquoi un Data Warehouse ?
Un Data Warehouse (entrepôt de données) centralise les données historiques issues de plu-
sieurs systèmes sources pour permettre l'analyse. Les caractéristiques d'un DW selon Inmon sont :
Orienté sujet : organisé autour des axes métier (ventes, clients, produits).
Intégré : données homogénéisées, formats uniformes.
Historisé : conservation de l'historique, pas de mise à jour destructive.
Non volatile : chargement en masse, lecture seule par les analystes.
4.2 Schéma en étoile (Star Schema)
Le schéma en étoile est la forme la plus courante en Business Intelligence. Il comprend :
Une table centrale de faits contenant les mesures numériques et les clés étrangères vers les
dimensions.
Des tables de dimensions autour, décrivant le contexte.
13
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
4.3 Schéma DW en étoile
DIM_DATE
DIM_CUSTOMER DIM_GEOGRAPHY
PK date_sk
full_date
PK customer_sk year / month / day PK geo_sk
customer_id weekday country
name region
email city
FACT_SALES
PK sale_id
FK customer_sk
FK product_sk
FK date_sk
FK geo_sk
FK status_sk
amount : DOUBLE
quantity : INT
discount : DOUBLE
DIM_PRODUCT
DIM_CHANNEL
PK product_sk DIM_STATUS
PK channel_sk
product_id
type
name
PK status_sk source
category
code
label
Figure 4.1 Schéma en étoile du Data Warehouse e-commerce
4.4 Table de faits : détail
Colonne Type Description
sale_id LONG Identiant unique de la ligne de fait
customer_sk LONG Clé surrogate vers DIM_CUSTOMER
product_sk LONG Clé surrogate vers DIM_PRODUCT
date_sk LONG Clé surrogate vers DIM_DATE
geo_sk LONG Clé surrogate vers DIM_GEOGRAPHY
status_sk LONG Clé surrogate vers DIM_STATUS
amount DOUBLE Mesure additive : montant
quantity INT Mesure additive : quantité
discount DOUBLE Mesure semi-additive : remise
Table 4.1 Structure de la table de faits FACT_SALES
4.5 Typologie des dimensions
Dimension conforme : partagée entre plusieurs faits (ex. DIM_DATE).
Cours OLAP temps réel 14
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Dimension dégénérée : clé technique sans description (ex. numéro de facture).
Dimension junk : agrégation de ags et statuts peu utilisés.
Role-playing : même dimension utilisée avec plusieurs rôles (DIM_DATE comme date de
commande ET date de livraison).
4.6 SCD : Slowly Changing Dimensions
Les dimensions évoluent dans le temps. Kimball propose plusieurs stratégies :
SCD Type 0 : gé, pas de mise à jour.
SCD Type 1 : écrasement pur (perte de l'historique).
SCD Type 2 : ajout d'une nouvelle ligne avec valid_from, valid_to, is_current.
SCD Type 3 : colonnes historiques (previous_value, current_value).
Astuce pratique
Dans Pinot, on privilégie le SCD Type 2 car on peut facilement ltrer les lignes actives via
WHERE is_current = true.
4.7 Schéma en ocon vs étoile
Le schéma en ocon (snowake ) décompose les dimensions en sous-dimensions normalisées.
Exemple : DIM_PRODUCT → DIM_CATEGORY → DIM_DEPARTMENT.
Avantage : économie d'espace, cohérence renforcée.
Inconvénient : plus de jointures, performance dégradée.
Pour Pinot, le schéma en étoile aplati (at) est le plus adapté : pas de jointure nécessaire
à l'exécution.
4.8 Schéma en ocon
DIM_GEO DIM_CUSTOMER DIM_DATE DIM_MONTH
PK customer_sk PK date_sk PK month_sk
PK geo_sk
customer_id full_date month_name
country, region
FK geo_sk FK month_sk FK year_sk
FACT_SALES
DIM_DEPT PK sale_id
FK customer_sk
DIM_YEAR
PK dept_sk PK year_sk
FK product_sk
dept_name year_value
FK date_sk
amount, quantity
DIM_CATEGORY DIM_PRODUCT
PK category_sk PK product_sk
name name
FK dept_sk FK category_sk
Figure 4.2 Schéma en ocon (snowake) : dimensions normalisées
Cours OLAP temps réel 15
Attention
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Le schéma en ocon est déconseillé pour Pinot : chaque niveau supplémentaire implique
une jointure, ce qui dégrade la latence. Dans Pinot on préfère le schéma en étoile aplati (at
star).
Cours OLAP temps réel 16
Chapitre 5
Conception ETL et mapping
5.1 Diagramme du pipeline ETL
Extract Transform Load
(Postgres / CSV) (mapping) (Pinot)
Source OLTP Logique métier Cible OLAP
Figure 5.1 Pipeline ETL Extract-Transform-Load
5.2 Extract : les sources
Connexion JDBC : lecture directe de Postgres.
Fichiers plats : CSV, Parquet sur S3/HDFS.
Kafka : ux temps réel d'événements.
API REST : appels HTTP vers des systèmes tiers.
5.3 Transform : les règles de transformation
Nettoyage : suppression des valeurs nulles, déduplication.
Normalisation : UPPER(), TRIM(), formats ISO.
Enrichissement : jointures avec les dimensions.
Agrégation : pré-calcul de sommes par jour/heure.
Dérivation : calcul de colonnes (year, month, quarter).
5.4 Load : les stratégies
Full load : rechargement complet de la table.
Incrémental : chargement des seuls nouveaux enregistrements (par date).
Upsert : mise à jour d'enregistrements existants (Pinot supporte l'upsert sur les tables REAL-
TIME).
17
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
5.5 Table de mapping ETL
Source (OLTP) Cible (Pinot) Transformation Type
[Link] order_id Copie directe LONG
orders.customer_id customer_id Copie directe LONG
[Link] amount Cast numeric → double DOUBLE
orders.created_at event_time Timestamp → epoch ms LONG
[Link] country Join + UPPER() STRING
[Link] category Join + normalisation STRING
[Link] status Mapping : 0→paid, 1→pending STRING
year Derived : YEAR(created_at) INT
month Derived : MONTH(created_at) INT
weekday Derived : DAYOFWEEK(created_at) INT
Table 5.1 Mapping ETL de Postgres vers Apache Pinot
Attention
Pinot ne supporte pas les JOINs entre tables par défaut (hors Lookup et Multi-Stage Engine ).
Le schéma doit donc être dénormalisé au moment du chargement.
5.6 Orchestration du pipeline
Pour orchestrer un ETL vers Pinot, plusieurs outils sont possibles :
Apache Airow : DAG Python pour la planication.
Apache NiFi : orchestration visuelle data ow.
Spring Batch : dans l'écosystème Spring Boot.
Kafka Connect + Pinot Sink : streaming natif.
Cours OLAP temps réel 18
Chapitre 6
Architecture interne d'Apache Pinot
6.1 Les composants du cluster
Un cluster Pinot est composé de cinq briques principales :
Controller : coordinateur du cluster, API REST d'administration.
Broker : reçoit les requêtes SQL, les découpe en sous-requêtes, agrège les résultats.
Server : stocke les segments, exécute les requêtes localement.
Minion (optionnel) : tâches de maintenance en arrière-plan (compaction, rebuild d'index).
Zookeeper : stocke les métadonnées (tables, schémas, états).
6.2 Segments : unité de stockage
Un segment est un chier immutable contenant un bloc de données. Toute la logique de Pinot
tourne autour de ce concept.
Un segment contient généralement quelques millions de lignes.
Il est compressé colonne par colonne.
Il est répliqué sur plusieurs serveurs (facteur de réplication).
Il est identié par un nom unique : orders_olap_OFFLINE_0, etc.
6.3 Tables OFFLINE, REALTIME, HYBRID
Type Source Cas d'usage
OFFLINE Batch (HDFS, S3, local) Historique, reporting
REALTIME Kafka, Pulsar, Kinesis Streaming, métriques vivantes
HYBRID Combinaison des deux Fusion historique + temps réel
Table 6.1 Types de tables Apache Pinot
6.4 Tenants et isolation
Un tenant est un groupe logique de serveurs. Il permet d'isoler des workloads :
Broker tenant : groupe de brokers dédiés.
Server tenant OFFLINE / REALTIME : serveurs spécialisés.
19
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
6.5 Cycle de vie d'un segment
CREATING CONSUMING ONLINE OFFLINE DELETED
init ush retention purge
Figure 6.1 Cycle de vie d'un segment Pinot (REALTIME)
6.6 Flux d'une requête SQL
Server 1
4. Résultat
1. SQL
Client JDBC Broker Server 2
Server 3
2. Sous-requêtes parallélisées / 3. Agrégation partielle
Figure 6.2 Flux d'exécution d'une requête SQL distribuée dans Pinot
Cours OLAP temps réel 20
Chapitre 7
Indexation avancée
7.1 Les types d'index Pinot
La force de Pinot réside dans la diversité de ses index.
7.1.1 Forward Index
Index par défaut : stocke les valeurs ligne par ligne pour chaque colonne. Accès rapide par
position.
7.1.2 Dictionary Encoding
Chaque valeur distincte est codée par un entier. Diminue drastiquement la taille pour les co-
lonnes à faible cardinalité.
7.1.3 Inverted Index
Map : valeur → liste des positions. Très ecace pour les ltres WHERE country = 'France'.
7.1.4 Sorted Index
Les lignes sont triées sur une colonne. Permet un accès par dichotomie et un stockage RLE
(run-length encoding) compact.
7.1.5 Range Index
Accélère les ltres BETWEEN x AND y sur les colonnes numériques.
7.1.6 Bloom Filter
Structure probabiliste : accélère les ltres = valeur sur les colonnes à haute cardinalité.
7.1.7 Star-Tree Index
Pré-agrégation multi-dimensionnelle stockée dans une arborescence. Permet de répondre à des
requêtes d'agrégation en O(log n) au lieu de O(n).
21
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
7.2 Choisir le bon index
Index Idéal pour À éviter si
Inverted Filtres d'égalité, cardinalité moyenne Cardinalité très élevée
Sorted Colonne monotone (time, id) Données réellement aléatoires
Range Filtres BETWEEN sur métrique Colonne catégorielle
Bloom Lookups ponctuels Range queries
Star-Tree GROUP BY fréquents Requêtes totalement ad-hoc
Table 7.1 Guide de choix d'index dans Apache Pinot
7.3 Star-Tree en détail
Le Star-Tree est une structure arborescente où chaque niveau représente une dimension de
dimensionsSplitOrder. Les feuilles contiennent des lignes pré-agrégées. Lors d'une requête GROUP
BY, Pinot choisit le n÷ud le plus bas compatible et évite de scanner les lignes individuelles.
Root (ALL)
country=FR country=DE country=MA
cat=Elec cat=Sport cat=Elec cat=Groc
Figure 7.1 Exemple de Star-Tree sur (country, category)
7.4 Exemple de conguration d'index
1 " tableIndexConfig " : {
2 " invertedIndexColumns " : [ " country " , " category " , " status " ] ,
3 " rangeIndexColumns " : [ " amount " ] ,
4 " sortedColumn " : [ " event_time " ] ,
5 " bloomFilterColumns " : [ " customer_id " ] ,
6 " starTreeIndexConfigs " : [
7 {
8 " dimensionsSplitOrder ": [ " country " , " category " ] ,
9 " functionColumnPairs " : [ " SUM__amount " , " COUNT__ * " ] ,
10 " maxLeafRecords " : 10000
11 }
12 ]
13 }
Listing 7.1 Extrait du table cong Pinot
Cours OLAP temps réel 22
Chapitre 8
Ingestion batch et temps réel
8.1 Ingestion batch
L'ingestion batch permet de charger de gros volumes historiques depuis :
Fichiers locaux : CSV, JSON, Avro, Parquet, ORC.
HDFS, S3, Azure Blob, GCS.
Le pipeline typique est décrit dans un chier YAML ingestion_spec.yaml. Pinot génère alors
des segments et les pousse sur le controller via SegmentTarPushJobRunner.
8.2 Ingestion temps réel via Kafka
1 " tableType " : " REALTIME " ,
2 " segmentsConfig " : {
3 " timeColumnName " : " event_time " ,
4 " schemaName " : " orders_olap " ,
5 " replication ": " 1 "
6 },
7 " streamConfigs " : {
8 " streamType " : " kafka " ,
9 " stream . kafka . topic . name " : " orders " ,
10 " stream . kafka . broker . list " : " kafka :9092 " ,
11 " stream . kafka . consumer . factory . class . name " :
12 " org . apache . pinot . plugin . stream . kafka20 . KafkaConsumerFactory " ,
13 " stream . kafka . decoder . class . name " :
14 " org . apache . pinot . plugin . stream . kafka . KafkaJSONMessageDecoder " ,
15 " realtime . segment . flush . threshold . rows " : " 100000 " ,
16 " realtime . segment . flush . threshold . time " : " 3600000 "
17 }
Listing 8.1 Stream cong Kafka pour Pinot REALTIME
8.3 Ingestion hybride
Une même table logique peut être alimentée simultanément en OFFLINE (historique) et REAL-
TIME (streaming). Pinot fusionne automatiquement les résultats à la lecture.
8.4 Upsert et déduplication
Depuis la v0.8, Pinot supporte l'upsert sur les tables REALTIME :
23
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
1 " upsertConfig " : {
2 " mode " : " FULL " ,
3 " primaryKeyColumn " : " order_id "
4 }
Listing 8.2 Conguration upsert
Cours OLAP temps réel 24
Chapitre 9
Intégration Apache Pinot avec Spring Boot
9.1 Architecture applicative
Couche Controller (REST API)
Couche Service (logique métier)
Couche Repository (accès données)
Client Apache Pinot (JDBC)
Postgres (JPA / Hibernate)
Figure 9.1 Architecture en couches Spring Boot + Pinot + Postgres
9.2 Dépendances Maven
1 < dependency >
2 < groupId > org . springframework . boot </ groupId >
3 < artifactId > spring - boot - starter - web </ artifactId >
4 </ dependency >
5 < dependency >
6 < groupId > org . springframework . boot </ groupId >
7 < artifactId > spring - boot - starter - data - jpa </ artifactId >
8 </ dependency >
9 < dependency >
10 < groupId > org . postgresql </ groupId >
11 < artifactId > postgresql </ artifactId >
12 </ dependency >
13 < dependency >
14 < groupId > org . apache . pinot </ groupId >
25
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
15 < artifactId > pinot - java - client </ artifactId >
16 < version > 1.0.0 </ version >
17 </ dependency >
Listing 9.1 Extrait du [Link]
9.3 Entités JPA côté OLTP
1 @Entity
2 @Table ( name = " orders " )
3 @Getter @Setter @NoArgsConstructor
4 public class Order {
5 @Id
6 @GeneratedValue ( strategy = GenerationType . IDENTITY )
7 private Long id ;
8
9 @Column ( name = " customer_id " )
10 private Long customerId ;
11
12 @Column ( name = " product_id " )
13 private Long productId ;
14
15 @Column ( precision = 10 , scale = 2)
16 private BigDecimal amount ;
17
18 private Integer status = 0;
19
20 @Column ( name = " created_at " )
21 private LocalDateTime createdAt = LocalDateTime . now () ;
22 }
Listing 9.2 Entité Order
9.4 Service Pinot
1 @Service
2 public class PinotAnalyticsService {
3
4 @Value ( " $ { pinot . broker . url : pinot - broker :8099} " )
5 private String brokerUrl ;
6
7 private Connection connection ;
8
9 @PostConstruct
10 public void init () {
11 this . connection = ConnectionFactory . fromHostList ( brokerUrl ) ;
12 }
13
14 public List < Map < String , Object > > topCountriesByRevenue ( int limit ) {
15 String query = " SELECT country , SUM ( amount ) AS ca " +
16 " FROM orders_olap GROUP BY country " +
17 " ORDER BY ca DESC LIMIT " + limit ;
18 return execute ( query ) ;
19 }
20 // ...
21 }
Listing 9.3 Service de requête Pinot en Spring Boot
Cours OLAP temps réel 26
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
9.5 Controllers REST
1 @RestController
2 @RequestMapping ( " / api / analytics " )
3 @RequiredArgsConstructor
4 public class AnalyticsController {
5 private final PinotAnalyticsService analyticsService ;
6
7 @GetMapping ( " / top - countries " )
8 public List < Map < String , Object > > topCountries (
9 @RequestParam ( defaultValue = " 10 " ) int limit ) {
10 return analyticsService . topCountriesByRevenue ( limit ) ;
11 }
12 }
Listing 9.4 AnalyticsController
9.6 Conguration : [Link]
1 spring :
2 datasource :
3 url : jdbc : postgresql : // postgres :5432/ pinotdb
4 username : pinotuser
5 password : pinotpass
6 jpa :
7 hibernate . ddl - auto : update
8
9 pinot :
10 broker :
11 url : pinot - broker :8099
12 controller :
13 url : http : // pinot - controller :9000
Listing 9.5 [Link]
Astuce pratique
La URL pinot-broker:8099 est un nom DNS résolu par Docker dans le réseau pinot-net.
En local hors conteneur, remplacer par localhost:8099.
Cours OLAP temps réel 27
Chapitre 10
Docker Compose et déploiement
10.1 Stack de déploiement
Spring Boot Postgres Zookeeper
:8080 :5432 :2181
Pinot Ctrl Pinot Broker Pinot Server
:9000 :8099 :7050
Réseau Docker : pinot-net
Figure 10.1 Topologie Docker Compose
10.2 Fichier [Link]
1 services :
2 postgres :
3 image : postgres :16 - alpine
4 environment :
5 POSTGRES_DB : pinotdb
6 POSTGRES_USER : pinotuser
7 POSTGRES_PASSWORD : pinotpass
8 ports : [ " 5432:5432 " ]
9 networks : [ pinot - net ]
10
11 zookeeper :
12 image : zookeeper :3.9
13 networks : [ pinot - net ]
14
15 pinot - controller :
16 image : apachepinot / pinot :1.0.0
17 command : " StartController - zkAddress zookeeper :2181 "
18 ports : [ " 9000:9000 " ]
19 depends_on : [ zookeeper ]
20 networks : [ pinot - net ]
21
22 pinot - broker :
23 image : apachepinot / pinot :1.0.0
24 command : " StartBroker - zkAddress zookeeper :2181 "
25 ports : [ " 8099:8099 " ]
26 depends_on : [ pinot - controller ]
28
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
27 networks : [ pinot - net ]
28
29 pinot - server :
30 image : apachepinot / pinot :1.0.0
31 command : " StartServer - zkAddress zookeeper :2181 "
32 depends_on : [ pinot - broker ]
33 networks : [ pinot - net ]
34
35 spring - app :
36 build : .
37 ports : [ " 8080:8080 " ]
38 depends_on : [ postgres , pinot - broker ]
39 networks : [ pinot - net ]
Listing 10.1 Extrait du [Link]
10.3 Commandes utiles
1 # Demarrer toute la stack
2 docker compose up -d -- build
3
4 # Verifier l ' etat
5 docker compose ps
6
7 # Suivre les logs de l ' application Spring Boot
8 docker compose logs -f spring - app
9
10 # Arreter et nettoyer ( volumes inclus )
11 docker compose down -v
Listing 10.2 Commandes Docker Compose
Astuce pratique
Le chier [Link] fourni dans le projet permet de lancer toute la stack avec
une seule commande : docker compose up -d.
Cours OLAP temps réel 29
Chapitre 11
Scripts OLTP et OLAP complets
11.1 Script OLTP (Postgres)
1 CREATE TABLE customers (
2 id BIGSERIAL PRIMARY KEY ,
3 name VARCHAR (100) NOT NULL ,
4 country VARCHAR (50) ,
5 created_at TIMESTAMP DEFAULT NOW ()
6 );
7
8 CREATE TABLE products (
9 id BIGSERIAL PRIMARY KEY ,
10 name VARCHAR (100) NOT NULL ,
11 category VARCHAR (50) ,
12 price NUMERIC (10 ,2)
13 );
14
15 CREATE TABLE orders (
16 id BIGSERIAL PRIMARY KEY ,
17 customer_id BIGINT REFERENCES customers ( id ) ,
18 product_id BIGINT REFERENCES products ( id ) ,
19 amount NUMERIC (10 ,2) ,
20 status INT DEFAULT 0 ,
21 created_at TIMESTAMP DEFAULT NOW ()
22 );
Listing 11.1 Création du schéma OLTP
11.2 Jeu de données de démonstration
1 INSERT INTO customers ( name , country ) VALUES
2 ( ' Alice Dupont ' , ' France ') ,
3 ( ' Bob Martin ' , ' France ') ,
4 ( ' Carlos Silva ' , ' Brazil ') ,
5 ( ' Hassan Tazi ' , ' Morocco ') ;
6
7 INSERT INTO products ( name , category , price ) VALUES
8 ( ' Laptop Pro 15 ' , ' Electronics ' , 1299.99) ,
9 ( ' Smartphone X ' , ' Electronics ' , 899.00) ,
10 ( ' Running Shoes ' , ' Sport ' , 120.50) ;
11
12 INSERT INTO orders ( customer_id , product_id , amount , status , created_at )
13 VALUES (1 , 1 , 1299.99 , 0 , NOW () - INTERVAL ' 10 days ') ;
Listing 11.2 Seed OLTP
30
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
11.3 Schéma Pinot (OLAP)
1 {
2 " schemaName " : " orders_olap " ,
3 " dimensionFieldSpecs " : [
4 { " name ": " order_id " , " dataType " : " LONG " } ,
5 { " name ": " customer_id " , " dataType " : " LONG " } ,
6 { " name ": " product_id " , " dataType " : " LONG " } ,
7 { " name ": " country " , " dataType " : " STRING " } ,
8 { " name ": " category " , " dataType " : " STRING " } ,
9 { " name ": " status " , " dataType " : " STRING " }
10 ],
11 " metricFieldSpecs " : [
12 { " name ": " amount " , " dataType " : " DOUBLE " }
13 ],
14 " dateTimeFieldSpecs " : [
15 {
16 " name " : " event_time " ,
17 " dataType " : " LONG " ,
18 " format " : " 1: MILLISECONDS : EPOCH " ,
19 " granularity " : " 1: MILLISECONDS "
20 }
21 ]
22 }
Listing 11.3 Schéma Pinot JSON
11.4 Table Pinot (OLAP)
1 {
2 " tableName " : " orders_olap " ,
3 " tableType " : " OFFLINE " ,
4 " segmentsConfig " : {
5 " timeColumnName " : " event_time " ,
6 " schemaName " : " orders_olap " ,
7 " replication " : " 1 "
8 },
9 " tenants " : {
10 " broker " : " DefaultTenant " ,
11 " server " : " DefaultTenant "
12 },
13 " tableIndexConfig " : {
14 " loadMode " : " MMAP " ,
15 " invertedIndexColumns " : [ " country " ," category " ," status " ] ,
16 " rangeIndexColumns " : [ " amount " ] ,
17 " sortedColumn " : [ " event_time " ] ,
18 " starTreeIndexConfigs " : [
19 {
20 " dimensionsSplitOrder " : [ " country " ," category " ] ,
21 " functionColumnPairs " : [ " SUM__amount " ," COUNT__ * " ] ,
22 " maxLeafRecords ": 10000
23 }
24 ]
25 }
26 }
Listing 11.4 Conguration de la table Pinot
11.5 Requêtes analytiques typiques
Cours OLAP temps réel 31
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
1 -- Top 10 pays par chiffre d ' affaires
2 SELECT country , SUM ( amount ) AS ca
3 FROM orders_olap
4 GROUP BY country
5 ORDER BY ca DESC
6 LIMIT 10;
7
8 -- Ventes par mois et par categorie
9 SELECT category ,
10 DATETIMECONVERT ( event_time ,
11 '1: MILLISECONDS : EPOCH ' ,
12 '1: DAYS : SIMPLE_DATE_FORMAT : yyyy - MM ' ,
13 '1: MONTHS ') AS month ,
14 COUNT (*) AS nb_orders
15 FROM orders_olap
16 GROUP BY category , month
17 ORDER BY month ;
Listing 11.5 Requêtes Pinot SQL
Cours OLAP temps réel 32
Chapitre 12
Performance et observabilité
12.1 Métriques clés à surveiller
Query latency : p50, p90, p99.
Ingestion lag : délai entre Kafka et Pinot.
Segment build time : temps de création d'un segment.
Heap / GC : JVM des serveurs Pinot.
Disk usage : croissance des segments.
12.2 Intégration Prometheus / Grafana
Pinot expose des métriques JMX qui peuvent être scrapées par un JMX Exporter et consommées
par Prometheus. Un dashboard Grafana ociel est disponible.
12.3 Optimisation des requêtes
Préférer un GROUP BY sur des colonnes indexées (Star-Tree).
Éviter LIKE '%xxx%' non anchored.
Utiliser LIMIT systématiquement.
Penser au pré-ltrage temporel via event_time.
Utiliser EXPLAIN PLAN FOR <query> pour comprendre l'exécution.
Éviter les sous-requêtes imbriquées non nécessaires.
Partitionner les tables REALTIME sur la clé primaire quand c'est possible.
33
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
12.4 Dashboard Grafana recommandé
Dashboard Grafana Pinot monitoring
Latence p99
QPS Broker Lag Kafka
des requêtes
Heap JVM Erreurs
Taille segments
Server ingestion
Figure 12.1 Panneaux typiques d'un dashboard de monitoring Pinot
12.5 Tuning des serveurs
Heap JVM : xer -Xms = -Xmx pour éviter les redimensionnements.
GC : G1GC recommandé pour les serveurs Pinot, ZGC pour des workloads à très faible
latence.
Nombre de threads : ajuster [Link] selon les c÷urs
CPU.
Segment size : viser 100 à 500 Mo par segment.
Cours OLAP temps réel 34
Chapitre 13
Sécurité
13.1 Authentication
Pinot supporte plusieurs mécanismes :
Basic Auth (utilisateur / mot de passe).
OAuth 2.0 / OIDC (via plugin).
JWT côté broker.
13.2 Autorisation au niveau table
Des access control factories permettent de restreindre l'accès par table et par utilisateur.
13.3 Chirement
TLS pour les communications inter-composants.
Chirement des segments au repos (via le stockage).
13.4 Bonnes pratiques côté Spring Boot
Ne jamais concaténer directement les paramètres utilisateurs dans la requête SQL (risque
d'injection).
Valider et borner les paramètres (ex. @Max(100) sur le limit).
Utiliser Spring Security pour exposer l'API d'analytics.
Logger les requêtes longues via AOP pour l'auditabilité.
1 @GetMapping ( " / top - products " )
2 public List < Map < String , Object > > topProducts (
3 @RequestParam ( defaultValue = " 10 " )
4 @Min (1) @Max (100) int limit ) {
5 return analyticsService . topProducts ( limit ) ;
6 }
Listing 13.1 Validation des paramètres côté API
35
Chapitre 14
Architecture Kafka + Pinot
14.1 Pourquoi Kafka avec Pinot ?
Kafka est le bus de messages standard pour le streaming distribué. Sa combinaison avec Pinot
permet de :
Découpler producteurs et consommateurs.
Conserver un log durable des événements.
Autoriser plusieurs consommateurs (Pinot, Spark, analyse ML).
Ingérer en continu avec une latence de quelques secondes.
14.2 Topologie de bout en bout
Spring Boot Pinot
Producer REALTIME table
Kafka Dashboard
topic : orders Grafana
Debezium CDC
Postgres Spark
OLTP ML pipeline
Figure 14.1 Architecture événementielle Kafka + Pinot
14.3 Conguration d'un producteur Spring Boot vers Kafka
1 @Service
2 public class OrderEventProducer {
3
4 private final KafkaTemplate < String , String > kafkaTemplate ;
5 private final ObjectMapper mapper ;
6
7 public void publish ( Order order ) {
8 try {
9 String json = mapper . writeValueAsString ( order ) ;
10 kafkaTemplate . send ( " orders " , order . getId () . toString () , json ) ;
11 } catch ( Exception e ) {
36
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
12 throw new RuntimeException ( " Kafka publish failed " , e ) ;
13 }
14 }
15 }
Listing 14.1 Producteur Spring Boot
14.4 Conguration Pinot REALTIME pour Kafka
1 {
2 " tableName " : " orders_olap " ,
3 " tableType " : " REALTIME " ,
4 " streamConfigs " : {
5 " streamType " : " kafka " ,
6 " stream . kafka . topic . name " : " orders " ,
7 " stream . kafka . broker . list " : " kafka :9092 " ,
8 " stream . kafka . consumer . factory . class . name " :
9 " org . apache . pinot . plugin . stream . kafka20 . KafkaConsumerFactory " ,
10 " stream . kafka . decoder . class . name " :
11 " org . apache . pinot . plugin . stream . kafka . KafkaJSONMessageDecoder " ,
12 " realtime . segment . flush . threshold . rows " : " 100000 " ,
13 " realtime . segment . flush . threshold . time " : " 1 h "
14 }
15 }
Listing 14.2 Table REALTIME Kafka
14.5 Débits et sizing
Quelques ordres de grandeur industriels :
Un broker Kafka bien conguré : 100 Mo/s d'ingestion par instance.
Un serveur Pinot : quelques millions de lignes par seconde en ingestion REALTIME.
Scaling horizontal en ajoutant des partitions Kafka et des serveurs Pinot.
14.6 Tolérance aux pannes
Kafka : réplication inter-brokers ([Link] = 3).
Pinot : réplication au niveau segment.
Stockage profond : pousse périodique des segments sur S3/HDFS.
Cours OLAP temps réel 37
Chapitre 15
Observabilité et monitoring avancés
15.1 La pyramide de l'observabilité
Traces
Logs
Métriques
Figure 15.1 Pyramide classique de l'observabilité
15.2 Logs structurés côté Spring
1 < configuration >
2 < appender name = " JSON " class = " ch . qos . logback . core . ConsoleAppender " >
3 < encoder class = " net . logstash . logback . encoder . LogstashEncoder " / >
4 </ appender >
5 < root level = " INFO " >
6 < appender - ref ref = " JSON " / >
7 </ root >
8 </ configuration >
Listing 15.1 Logback JSON pour une ingestion Loki
15.3 Tracing distribué
Avec OpenTelemetry, chaque requête peut être suivie à travers les couches Spring → Pinot
→ Kafka. Le span d'une requête SQL inclut la latence côté broker et la latence côté server.
15.4 Alerting
Alerte : latence p99 > 500 ms pendant 5 minutes.
38
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Alerte : lag Kafka > 30 s.
Alerte : taux d'erreur API > 1 %.
Cours OLAP temps réel 39
Chapitre 16
Cas d'étude et exercices
16.1 Cas d'étude : tableau de bord e-commerce
Un site e-commerce souhaite proposer à ses marchands un tableau de bord temps réel indiquant,
pour chaque vendeur :
Le chire d'aaires du jour.
Les 10 produits les plus vendus.
Le nombre de commandes en attente.
La répartition géographique des ventes.
Architecture cible : Postgres (source) → Kafka (événements) → Pinot (OLAP REALTIME)
→ Spring Boot API → Dashboard front.
16.2 TP 1 : mise en place de la stack
TP 1
Objectif : lancer la stack complète et vérier l'accès à Postgres et à Pinot.
1. Cloner le projet.
2. Lancer docker compose up -d build.
3. Vérier les endpoints : [Link] (Pinot), [Link]
(Spring).
4. Insérer un client via l'API Spring.
16.3 TP 2 : ingestion des données dans Pinot
TP 2
Objectif : charger dans Pinot le chier CSV exporté depuis Postgres.
1. Exporter orders_olap.csv.
2. Créer le schéma et la table Pinot.
3. Lancer l'ingestion batch.
4. Exécuter une requête SQL via la console.
40
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
16.4 TP 3 : API d'analytics
TP 3
Objectif : construire un nouvel endpoint /api/analytics/top-products.
1. Ajouter la méthode dans PinotAnalyticsService.
2. Exposer l'endpoint dans AnalyticsController.
3. Tester avec curl.
16.5 TP 4 : ingestion temps réel Kafka
TP 4 (approfondissement)
Objectif : ajouter Kafka au docker-compose et ingérer un ux d'événements dans Pinot.
1. Ajouter les services kafka et kafka-ui.
2. Créer un topic orders.
3. Modier la table Pinot en REALTIME.
4. Produire des messages JSON depuis Spring Boot.
5. Vérier l'ingestion quasi-immédiate.
16.6 Questions de révision
1. Quelles sont les cinq briques d'un cluster Pinot et leurs rôles ?
2. Quelle est la diérence entre un schéma en étoile et un schéma en ocon ?
3. Pourquoi Pinot n'est-il pas adapté à l'OLTP ?
4. Quels sont les cas d'usage typiques d'un index Star-Tree ?
5. Décrivez les étapes d'un pipeline ETL Postgres → Pinot.
6. Quelle stratégie SCD retenir lorsqu'on veut conserver l'historique d'une dimension ?
7. Quelle est la diérence entre une table OFFLINE, REALTIME et HYBRID ?
8. Pourquoi dénormalise-t-on le schéma avant de charger dans Pinot ?
9. Quel rôle joue Zookeeper dans le cluster ?
10. Comment Pinot répond-il à une requête GROUP BY en utilisant un Star-Tree ?
16.7 Éléments de correction
1. Composants : Controller (coordination), Broker (dispatch des requêtes), Server (stockage
et exécution), Minion (tâches de maintenance), Zookeeper (métadonnées et états).
2. Étoile vs ocon : l'étoile a des dimensions aplaties (une seule jointure), le ocon normalise
les dimensions (plusieurs niveaux).
3. OLTP inadapté : Pinot n'ore pas de transactions ACID, pas de mise à jour ligne à ligne
performante, pas de contraintes référentielles.
4. Star-Tree : pré-agrège les mesures selon un ordre de dimensions. Idéal pour des GROUP BY
récurrents avec SUM, COUNT, MIN, MAX.
5. ETL Postgres → Pinot : (1) EXTRACT via SQL, (2) TRANSFORM en CSV/Avro dé-
normalisé, (3) LOAD via LaunchDataIngestionJob ou Kafka.
Cours OLAP temps réel 41
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
6. SCD Type 2 : ajoute une ligne à chaque évolution et gère valid_from/valid_to/is_current.
7. OFFLINE / REALTIME / HYBRID : batch, streaming, union des deux. HYBRID
permet l'historique profond + la fraîcheur temps réel.
8. Dénormalisation : Pinot scanne une seule table. La jointure au runtime serait coûteuse. On
la fait en amont (ETL) une seule fois.
9. Zookeeper : stocke les schémas, la table cong, la liste des segments et leur état, coordonne
les leaders.
10. Star-Tree + GROUP BY : Pinot descend dans l'arbre jusqu'au n÷ud pré-agrégé le plus
spécique couvrant le ltre, puis renvoie directement l'agrégat sans scanner les lignes de
détail.
Cours OLAP temps réel 42
Chapitre 17
Multi-Stage Query Engine
17.1 Limites du moteur historique
Le moteur de requête historique de Pinot (v2 engine) :
N'exécute les requêtes que sur une seule table.
Ne supporte pas les jointures shue entre tables distribuées.
Se limite aux agrégations sur la table scannée.
17.2 Le Multi-Stage Engine (v2+)
Introduit en 2022, il décompose une requête en plusieurs étapes (stages) exécutées en parallèle
sur le cluster. Il supporte :
Les jointures INNER JOIN, LEFT JOIN entre tables.
Les sous-requêtes imbriquées.
Les fenêtres (WINDOW) analytiques.
Les CTE (WITH ... AS).
17.3 Activer le Multi-Stage Engine
1 SET useMultistageEngine = true ;
2
3 SELECT o . country , p . category , SUM ( o. amount )
4 FROM orders_olap o
5 JOIN products_dim p ON p . product_id = o . product_id
6 GROUP BY o . country , p . category
7 ORDER BY SUM ( o . amount ) DESC ;
Listing 17.1 Activation via option
17.4 Flux d'exécution multi-stage
Stage 1 : Stage 2 : Stage 3 : Stage 4 :
Scan orders_olap Scan products_dim Join + GroupBy Sort + Limit
Figure 17.1 Pipeline d'exécution du Multi-Stage Engine
43
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Astuce pratique
Le Multi-Stage Engine ouvre la porte à un vrai usage de Pinot comme Data Warehouse
interactif, proche d'un Snowake. Les jointures distribuées restent toutefois plus coûteuses
que le scan d'une table dénormalisée.
17.5 Cas d'usage du Multi-Stage
Data Warehouse fédéré : dimensions externes mises à jour fréquemment.
Self-service analytics : laisser l'analyste faire ses propres joins.
Audit trail : joindre logs et dimension utilisateurs.
Cours OLAP temps réel 44
Chapitre 18
Patterns d'architecture avec Pinot
18.1 Pattern 1 : Lambda Architecture
Batch Layer
(S3/HDFS)
Pinot
Sources Dashboard
HYBRID
Speed Layer
(Kafka)
Figure 18.1 Lambda Architecture avec Pinot HYBRID
18.2 Pattern 2 : CQRS
Command Query Responsibility Segregation : séparer les écritures (OLTP Postgres) et
les lectures analytiques (OLAP Pinot). Spring Boot applique la commande sur Postgres, émet un
événement Kafka, et Pinot met à jour sa vue OLAP.
18.3 Pattern 3 : Event Sourcing
Chaque changement d'état est un événement immutable poussé dans Kafka. Pinot reconstruit
la vue matérialisée en continu. Idéal pour l'audit, le debugging historique et la conformité régle-
mentaire.
18.4 Pattern 4 : Funnel Analytics
Typique des produits web : on veut savoir combien d'utilisateurs passent de l'étape A à l'étape
B puis C. Pinot fournit la fonction FUNNEL_COUNT pour ces cas d'usage.
1 SELECT FUNNEL_COUNT (
2 STEPS ( event_name = ' view_product ' ,
3 event_name = ' add_to_cart ' ,
4 event_name = ' purchase ') ,
5 CORRELATE_BY ( user_id ) ,
6 SETTINGS ( ' window =86400000 ')
7 ) AS steps
8 FROM user_events
9 WHERE event_time > ago ( '1 d ') ;
45
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
Listing 18.1 Exemple de funnel
Cours OLAP temps réel 46
Chapitre 19
Conclusion
Synthèse
Ce cours a présenté Apache Pinot de bout en bout : de la théorie OLAP/OLTP à l'inté-
gration avec Spring Boot, en passant par le design ETL, la modélisation relationnelle 3NF,
la modélisation dimensionnelle en étoile et le déploiement via Docker Compose. Le projet
pratique fourni permet de tout expérimenter localement.
19.1 Pour aller plus loin
Documentation ocielle : [Link]
StarTree Cloud : ore managée.
Livre The Data Warehouse Toolkit de Ralph Kimball.
Designing Data-Intensive Applications de Martin Kleppmann.
Conférences : Apache Con, Current (Conuent), Data Council.
19.2 Remerciements
Dr. BADR EL KHALYLY
Merci pour votre attention
Année universitaire 20252026
47
Chapitre 20
Glossaire
ACID
Atomicity, Consistency, Isolation, Durability propriétés transactionnelles des SGBDR clas-
siques.
BASE
Basically Available, Soft state, Eventual consistency modèle des systèmes NoSQL distribués.
Broker
Composant Pinot recevant les requêtes SQL et les distribuant aux serveurs.
CTE
Common Table Expression sous-requête nommée dans une clause WITH.
CQRS
Command Query Responsibility Segregation séparation des modèles d'écriture et de lecture.
Dimension
Table décrivant le contexte d'un fait (client, produit, temps).
Data Warehouse
Entrepôt de données centralisé pour l'analyse.
ETL
Extract, Transform, Load pipeline d'alimentation d'un DW.
ELT
Extract, Load, Transform variante où la transformation a lieu après le chargement.
Fait Mesure quantitative centrale d'un schéma dimensionnel.
Granularité
Niveau de détail du grain d'une table de faits.
HDFS
Hadoop Distributed File System stockage distribué utilisé comme stockage profond.
HYBRID
Table Pinot combinant OFFLINE et REALTIME.
JDBC
Java Database Connectivity API standard d'accès aux bases.
Kafka
Bus de messages distribué, utilisé pour le streaming temps réel.
Multi-Stage Engine
Moteur Pinot v2+ supportant les jointures distribuées.
OLAP
Online Analytical Processing traitement analytique en ligne.
48
Apache Pinot + Spring Boot Dr. BADR EL KHALYLY
OLTP
Online Transaction Processing traitement transactionnel en ligne.
Segment
Unité immutable de stockage d'une table Pinot.
SCD
Slowly Changing Dimension gestion de l'historique des dimensions.
Schéma en étoile
Modèle dimensionnel avec une table centrale de faits et des dimensions aplaties autour.
Schéma en ocon
Variante normalisée où les dimensions sont éclatées en sous-tables.
Star-Tree
Index de pré-agrégation multi-dimensionnelle propre à Pinot.
Tenant
Groupe logique de ressources Pinot permettant l'isolation.
Upsert
Opération combinant INSERT et UPDATE, supportée sur les tables REALTIME.
Zookeeper
Service de coordination distribuée, support métadonnées du cluster Pinot.
Cours OLAP temps réel 49
Chapitre 21
Bibliographie et ressources
21.1 Livres de référence
Ralph Kimball, The Data Warehouse Toolkit, 3e édition, Wiley, 2013.
W. H. Inmon, Building the Data Warehouse, 4e édition, Wiley, 2005.
Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly, 2017.
Craig Walls, Spring in Action, 6e édition, Manning, 2022.
21.2 Documentation ocielle
Apache Pinot : [Link]
Apache Kafka : [Link]
Spring Boot : [Link]
Docker Compose : [Link]
21.3 Articles et conférences
LinkedIn Engineering Blog : Introducing Pinot .
Uber Engineering : Real-time Analytics at Uber .
StarTree Blog : [Link]
21.4 Communauté
Slack Apache Pinot : [Link].
GitHub : [Link]
Conférences : ApacheCon, Current, Data Council.
Ce document a été rédigé et mis en page avec LATEX par
Dr. BADR EL KHALYLY
Cours Apache Pinot + Spring Boot 2025/2026
50