Module 8 : Bonnes Pratiques et Optimisation
Architecture Medallion (Bronze/Silver/Gold)
L'architecture en couches est la référence pour organiser les données dans un Data Lake :
+------------------------------------------------------------------+
| ARCHITECTURE MEDALLION |
+------------------------------------------------------------------+
| |
| BRONZE (Raw) SILVER (Cleaned) GOLD (Business) |
| +-------------+ +-------------+ +-------------+ |
| | Données | --> | Données | --> | Agrégations | |
| | brutes | | nettoyées | | métriques | |
| | (as-is) | | validees | | KPIs | |
| +-------------+ +-------------+ +-------------+ |
| |
| Format: JSON/CSV Format: Parquet Format: Delta/ |
| Partitions: date Partitions: optimisé SQL Pool |
| |
+------------------------------------------------------------------+
Exemple de structure
/datalake/
/bronze/
/erp/
/orders/2024/01/01/orders_20240101.json
/customers/2024/01/01/customers_20240101.csv
/crm/
/contacts/...
/silver/
/sales/
/orders/year=2024/month=01/part-00000.parquet
/customers/part-00000.parquet
/gold/
/analytics/
/daily_sales/
/customer_segments/
/product_performance/
Optimisation SQL Pool Dédié
1. Choix de la distribution
Règles de base :
- HASH : Tables > 60 millions de lignes, colonnes de jointure frequentes
- REPLICATE : Tables < 2GB, dimensions stables
- ROUND_ROBIN : Tables de staging, chargements temporaires
-- Analyser la distribution actuelle
SELECT
t.name AS table_name,
d.distribution_policy_desc,
d.total_space_MB,
d.rows_per_distribution_MIN,
d.rows_per_distribution_MAX,
d.skew_percentage
FROM
(
SELECT
object_id,
distribution_policy_desc,
SUM(reserved_space_MB) AS total_space_MB,
MIN(row_count) AS rows_per_distribution_MIN,
MAX(row_count) AS rows_per_distribution_MAX,
100.0 * (MAX(row_count) - MIN(row_count)) / NULLIF(MAX(row_count), 0) AS skew_percentage
FROM sys.dm_pdw_nodes_db_partition_stats
GROUP BY object_id, distribution_policy_desc
) d
JOIN sys.tables t ON d.object_id = t.object_id
ORDER BY skew_percentage DESC;
2. Index et statistiques
-- Clustered Columnstore Index (par défaut, optimal pour analytique)
CREATE TABLE dbo.FactVentes (...)
WITH (CLUSTERED COLUMNSTORE INDEX);
-- Heap pour le staging rapide
CREATE TABLE dbo.StagingVentes (...)
WITH (HEAP);
-- Créer des statistiques
CREATE STATISTICS stats_client ON dbo.FactVentes(ClientID);
CREATE STATISTICS stats_date ON dbo.FactVentes(DateVente);
CREATE STATISTICS stats_multi ON dbo.FactVentes(ClientID, DateVente);
-- Mettre à jour les statistiques
UPDATE STATISTICS dbo.FactVentes;
3. Gestion des ressources
-- Voir les DWU actuels
SELECT
db.name,
ds.edition,
ds.service_objective
FROM sys.database_service_objectives ds
JOIN sys.databases db ON ds.database_id = db.database_id;
-- Classes de ressources
-- smallrc: 3% des ressources (défaut)
-- mediumrc: 10%
-- largerc: 22%
-- xlargerc: 70%
-- Assigner un utilisateur a une classe
EXEC sp_addrolemember 'largerc', 'ETLUser';
-- Vérifier l'allocation
SELECT * FROM sys.dm_pdw_exec_sessions
WHERE status = 'Active';
4. Optimisation des requêtes
-- Analyser le plan d'exécution
EXPLAIN
SELECT c.Nom, SUM(v.Montant)
FROM FactVentes v
JOIN DimClient c ON v.ClientID = c.ClientID
GROUP BY c.Nom;
-- Identifier les requêtes lentes
SELECT TOP 20
request_id,
submit_time,
total_elapsed_time / 1000.0 AS duration_sec,
[label],
command
FROM sys.dm_pdw_exec_requests
WHERE status = 'Completed'
ORDER BY total_elapsed_time DESC;
Optimisation SQL Serverless
1. Format des fichiers
| Format | Performance | Coût | Recommandation |
|---|---|---|---|
| Parquet | Excellent | Bas | Préféré |
| Delta | Excellent | Bas | Si mises à jour |
| CSV | Faible | Élevé | Éviter si possible |
| JSON | Faible | Élevé | Bronze layer uniquement |
2. Partitionnement
-- Structure de fichiers partitionnés
/data/ventes/year=2024/month=01/day=01/*.parquet
/data/ventes/year=2024/month=01/day=02/*.parquet
-- Requête avec pruning automatique
SELECT *
FROM OPENROWSET(
BULK 'https://storage.dfs.core.windows.net/data/ventes/year=*/month=*/day=*/*.parquet',
FORMAT = 'PARQUET'
) AS ventes
WHERE ventes.filepath(1) = '2024' -- Filtre sur l'année
AND ventes.filepath(2) = '01'; -- Filtre sur le mois
3. Spécification du schéma
-- Définir le schéma explicitement (plus performant)
SELECT *
FROM OPENROWSET(
BULK 'https://storage.dfs.core.windows.net/data/*.parquet',
FORMAT = 'PARQUET'
)
WITH (
OrderID INT,
CustomerID INT,
OrderDate DATE,
Amount DECIMAL(18,2)
) AS orders;
Optimisation Spark
1. Configuration du cluster
# Configuration Spark recommandée
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true")
spark.conf.set("spark.sql.shuffle.partitions", "auto")
# Pour les jointures
spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")
2. Bonnes pratiques PySpark
# BIEN: Operations colonnes natives
from pyspark.sql.functions import col, when, sum
df = df.withColumn("category",
when(col("amount") > 1000, "High")
.when(col("amount") > 100, "Medium")
.otherwise("Low")
)
# MAUVAIS: UDF Python (lent)
@udf
def categorize(amount):
if amount > 1000: return "High"
elif amount > 100: return "Medium"
else: return "Low"
# BIEN: Filtrer tôt
df_filtered = df.filter(col("date") >= "2024-01-01").select("id", "amount")
# MAUVAIS: Filtrer tard
df_all = df.select("*") # Charge tout
df_filtered = df_all.filter(col("date") >= "2024-01-01")
# BIEN: Broadcast pour petites tables
from pyspark.sql.functions import broadcast
result = big_df.join(broadcast(small_df), "key")
# Cache si réutilisation
df.cache() # ou df.persist(StorageLevel.MEMORY_AND_DISK)
3. Delta Lake
# Optimiser les fichiers Delta
spark.sql("OPTIMIZE delta.`/path/to/table`")
# Compacter les petits fichiers
spark.sql("OPTIMIZE delta.`/path/to/table` ZORDER BY (customer_id)")
# Nettoyer les anciennes versions
spark.sql("VACUUM delta.`/path/to/table` RETAIN 168 HOURS")
Gestion des coûts
SQL Pool Dédié
- Mettre en pause le pool quand inutilisé
- Utiliser l'auto-pause si disponible
- Dimensionner selon les besoins réels (DWU)
- Planifier les workloads lourds en heures creuses
-- Script pour pause automatique (Azure Automation)
# PowerShell
$pool = Get-AzSynapseSqlPool -WorkspaceName "myworkspace" -Name "mypool"
if ($pool.Status -eq "Online") {
Suspend-AzSynapseSqlPool -WorkspaceName "myworkspace" -Name "mypool"
}
SQL Serverless
- Utiliser Parquet plutôt que CSV (10x moins de données scannées)
- Partitionner les données par date
- Sélectionner uniquement les colonnes nécessaires
- Créer des vues pour contrôler les accès
Spark Pools
- Activer l'auto-scale
- Configurer l'auto-pause (ex: 15 min d'inactivité)
- Utiliser des petits nodes pour le dev, grands pour la prod
- Préférer les jobs planifiés aux notebooks interactifs
Monitoring et alertes
-- Requêtes en cours d'exécution
SELECT * FROM sys.dm_pdw_exec_requests
WHERE status NOT IN ('Completed', 'Failed', 'Cancelled');
-- Utilisation des ressources
SELECT * FROM sys.dm_pdw_nodes_resource_governor_resource_pools;
-- Attentes
SELECT * FROM sys.dm_pdw_waits WHERE state = 'Granted';
-- Configurer des alertes Azure Monitor
-- 1. Metric: DWU utilisation > 80%
-- 2. Metric: Nombre de connexions actives
-- 3. Log alert: Requêtes > 10 minutes
Checklist de performance
Avant mise en production :
- [ ] Tables distribuées correctement (pas de skew > 10%)
- [ ] Statistiques à jour sur toutes les tables
- [ ] Index columnstore sur les tables de faits
- [ ] Tables de dimension répliquées
- [ ] Données partitionnées dans le Data Lake
- [ ] Format Parquet/Delta utilisé
- [ ] Result set caching activé
- [ ] Classes de ressources configurées
- [ ] Monitoring et alertes en place
- [ ] Plan de pause/reprise documenté