← Retour au blog

Evaluer et reduire l'empreinte carbone de la data

Lucian BLETAN

La sobriete numerique est un levier a la fois economique et environnemental. La regle d’or: mesurer d’abord, agir ensuite. Sans metrologie fiable, on optimise a l’aveugle. Avec des mesures simples et reproductibles (kWh, cout, volumes), on priorise les gros postes et on documente les gains. Cette page propose un cadre concret: instrumentation minimale, requetes utiles, leviers d’optimisation et arbitrages “valeur x kWh”.

prerequis

  • une base de metrologie (meme simple) pour stocker energie, volumes et couts.
  • des etiquettes par job/requete (owner, produit, fonctionnalite) pour agreger au bon niveau.
  • un facteur carbone par region ou datacenter (gCO2e/kWh) pour estimer l’empreinte.
  • un cycle d’amelioration continue (revue mensuelle, top 3 postes).

apercu rapide

  • instrumenter compute, stockage, transferts avec des schemas stables.
  • exposer des tableaux de bord par produit et par fonctionnalite.
  • cibler les 3 postes majeurs, pas les micro-optimisations.
  • appliquer des leviers simples: formats columnaires, compression, caches, TTL.
  • industrialiser les arrets planifies et la mise en veille des ressources.
  • rendre visibles les couts et les kWh aux equipes pour guider les choix.

cartographier les postes d’empreinte

empreinte data

compute

etl batch

requetes ad hoc

inference modele

stockage

historiques froids

snapshots et copies

transferts

egress inter region

exports repetitifs

applicatif

caches sous tailles

jobs orphelins

modeles de donnees pour mesurer

tables minimales

-- energie et cout par job (horodatage et etiquette produit/feature)
CREATE TABLE IF NOT EXISTS finops_energy (
  ts TIMESTAMP NOT NULL,
  job TEXT NOT NULL,
  product TEXT NOT NULL,
  feature TEXT NOT NULL,
  kwh DOUBLE PRECISION NOT NULL,
  cost_eur DOUBLE PRECISION NOT NULL,
  region TEXT,
  carbon_intensity_g_per_kwh DOUBLE PRECISION DEFAULT 400
);

-- transferts reseau sortants (egress)
CREATE TABLE IF NOT EXISTS finops_transfer (
  ts TIMESTAMP NOT NULL,
  flow TEXT NOT NULL,
  product TEXT NOT NULL,
  feature TEXT NOT NULL,
  gb DOUBLE PRECISION NOT NULL,
  cost_eur DOUBLE PRECISION NOT NULL,
  region TEXT
);

-- stockage par classe (chaud/tiède/froid) et par produit
CREATE TABLE IF NOT EXISTS finops_storage (
  day DATE NOT NULL,
  product TEXT NOT NULL,
  feature TEXT NOT NULL,
  class TEXT CHECK (class IN ('hot','warm','cold')),
  tib DOUBLE PRECISION NOT NULL,
  cost_eur DOUBLE PRECISION NOT NULL,
  region TEXT
);

etiquetage: discipline utile

-- exemple d'etiquettes au lancement d'un job ETL
INSERT INTO finops_energy(ts, job, product, feature, kwh, cost_eur, region, carbon_intensity_g_per_kwh)
VALUES (NOW(), 'daily_orders_etl', 'commerce', 'orders', 12.4, 2.90, 'eu-west', 300);

mesurer: requetes pratiques

  • energie et cout par job et sur 30 jours
  • empreinte carbone associee (kgCO2e)
  • cout par utilisateur et par fonctionnalite
  • transferts sortants a surveiller
-- top jobs par kWh sur 30 jours
SELECT job,
       ROUND(SUM(kwh), 2) AS kwh,
       ROUND(SUM(cost_eur), 2) AS cost
FROM finops_energy
WHERE ts >= CURRENT_DATE - INTERVAL '30 day'
GROUP BY job
ORDER BY kwh DESC
LIMIT 20;

-- estimation kgCO2e par produit
SELECT product,
       ROUND(SUM(kwh * carbon_intensity_g_per_kwh) / 1000.0, 1) AS kgco2e,
       ROUND(SUM(cost_eur), 2) AS cost
FROM finops_energy
WHERE ts >= CURRENT_DATE - INTERVAL '30 day'
GROUP BY product
ORDER BY kgco2e DESC;

-- cout par fonctionnalite (energy + transfer + storage)
WITH e AS (
  SELECT product, feature, SUM(cost_eur) AS c FROM finops_energy
  WHERE ts >= CURRENT_DATE - INTERVAL '30 day' GROUP BY 1,2
),
t AS (
  SELECT product, feature, SUM(cost_eur) AS c FROM finops_transfer
  WHERE ts >= CURRENT_DATE - INTERVAL '30 day' GROUP BY 1,2
),
s AS (
  SELECT product, feature, SUM(cost_eur) AS c FROM finops_storage
  WHERE day >= CURRENT_DATE - INTERVAL '30 day' GROUP BY 1,2
)
SELECT COALESCE(e.product,t.product,s.product) AS product,
       COALESCE(e.feature,t.feature,s.feature) AS feature,
       ROUND(COALESCE(e.c,0)+COALESCE(t.c,0)+COALESCE(s.c,0),2) AS cost_30d_eur
FROM e FULL OUTER JOIN t USING(product,feature)
      FULL OUTER JOIN s USING(product,feature)
ORDER BY cost_30d_eur DESC
LIMIT 20;

-- transferts sortants eleves (egress)
SELECT flow, product, feature,
       ROUND(SUM(gb),1) AS gb_30d, ROUND(SUM(cost_eur),2) AS cost_30d_eur
FROM finops_transfer
WHERE ts >= CURRENT_DATE - INTERVAL '30 day'
GROUP BY flow, product, feature
ORDER BY gb_30d DESC
LIMIT 20;

reduire: leviers concrets

  • formats columnaires et compression (parquet, orc) pour les tables analytiques.
  • vues aggregees et caches pour les requetes repetitives.
  • modeles plus petits (distilles, quantifies) pour l’inference.
  • arrets planifies et mises en veille automatiques des ressources.
  • retention et TTL adaptes: purger ce qui ne sert plus.
# exemples generiques (pseudo)
# compression logs applicatifs
rotate_logs --gzip --max-age 14d

# purge donnees temporaires > 7 jours
delete_old --path s3://data/tmp --older-than 7d

# plan d'extinction hors heures pleines
schedule_shutdown --group etl-workers --weekdays 20:00-06:00 --weekends all

activer des caches et des vues aggregees

-- vue aggregee pour un tableau de bord tres consomme
CREATE MATERIALIZED VIEW IF NOT EXISTS bi.orders_daily AS
SELECT order_date::date AS d, country, COUNT(*) AS n, SUM(amount_eur) AS revenue
FROM raw.orders
WHERE order_date >= CURRENT_DATE - INTERVAL '180 day'
GROUP BY 1,2;

-- rafraichissement planifie
-- (utiliser le planificateur de votre SGBD ou un orchestrateur)
REFRESH MATERIALIZED VIEW bi.orders_daily;

quantifier et distiller les modeles

# estimation simple du gain de cout vs latence
def gain(lat_ms_before, lat_ms_after, cpu_w_before, cpu_w_after, qps):
    # kWh par heure ~ (W * h) / 1000 ; ici on compare puissance approx
    before = cpu_w_before * qps * lat_ms_before / 1000.0
    after  = cpu_w_after  * qps * lat_ms_after  / 1000.0
    return {"delta_power_w_ms": before - after, "improvement_pct": 100.0 * (before - after) / max(before, 1e-9)}

arbitrer: valeur x kWh

  • matrice “valeur vs kWh”: prioriser ce qui consomme beaucoup et apporte peu.
  • exiger une preuve de valeur avant d’allouer davantage de ressources.
  • rendre visibles couts et kWh aux equipes: ownership et responsabilite.

mesurer

identifier top 3 postes

designer leviers

experimenter et comparer

generaliser si gain prouve

suivre et reevaluer

gabarit de fiche “valeur x kWh”

feature: "recalcul dashboard temps reel"
benefice_attendu: "latence -50% pour 120 utilisateurs"
cout_30j_eur: 3200
kwh_30j: 950
kgco2e_30j: 280
leviers_testes:
  - "cache 5 min" : gain "kwh -35%, latence P95 -40%"
  - "agregations horaire" : gain "kwh -50%, latence P95 -55%"
decision: "deployer agregations horaire, revoir besoin temps reel"
owner: "@bi-team"
revue: "mois prochain"

tutoriel pas-a-pas

etape 1: instrumenter

  • collecter kWh et couts par job.
  • enregistrer transferts et stockage par produit/feature.
  • associer un facteur carbone par region.
-- exemple: enrichir chaque ligne d'energie avec un facteur carbone
UPDATE finops_energy
SET carbon_intensity_g_per_kwh = 300
WHERE region = 'eu-west';

etape 2: etiqueter

  • imposer product et feature sur tous les jobs et pipelines.
  • refuser l’execution si les etiquettes sont absentes.
# pseudo hook d'orchestrateur
[ -z "$PRODUCT" ] && echo "PRODUCT manquant" && exit 1
[ -z "$FEATURE" ] && echo "FEATURE manquant" && exit 1

etape 3: analyser et prioriser

  • sortir les top 20 consommateurs.
  • qualifier valeur metier vs cout/empreinte.
-- top consommateurs par feature
SELECT product, feature, ROUND(SUM(kwh),1) AS kwh_30d, ROUND(SUM(cost_eur),2) AS cost_30d
FROM finops_energy
WHERE ts >= CURRENT_DATE - INTERVAL '30 day'
GROUP BY 1,2
ORDER BY kwh_30d DESC
LIMIT 20;

etape 4: agir

  • appliquer formats columnaires, compression, vues aggregees.
  • reduire la resolution des donnees si la precision est surabondante.
  • rationaliser les exports repetitifs (regrouper, dedupliquer).
-- exemple: echantillonnage raisonnable pour exploration
CREATE TABLE IF NOT EXISTS raw.events_sampled AS
SELECT * FROM raw.events
WHERE random() < 0.1;

etape 5: verifier et documenter les gains

  • comparer kWh et latence avant/apres.
  • consigner les changements et les preuves.
change_log:
  id: "opt-2024-11-dashboard_orders"
  before:
    kwh_30d: 950
    p95_ms: 1800
  after:
    kwh_30d: 520
    p95_ms: 940
  decision: "generalisation"
  owner: "@bi-team"

exemples complets

cas 1: passer de json a parquet

-- table de travail json brute
CREATE TABLE raw.events_json AS
SELECT *
FROM external_raw_json; -- source externe en json

-- table columnnaire (parquet) avec compression
CREATE TABLE raw.events_parquet
WITH (storage_format = 'parquet', compression = 'snappy') AS
SELECT *
FROM raw.events_json;

-- gain attendu: IO reduit, scans plus rapides, kWh en baisse

cas 2: TTL et retentions

-- table partitionnee par jour avec retention sur 90 jours pour le chaud
CREATE TABLE IF NOT EXISTS logs.app_events (
  day DATE,
  ts TIMESTAMP,
  user_id TEXT,
  event TEXT,
  payload JSONB
) PARTITION BY RANGE (day);

-- politique de purge (adaptation selon SGBD)
-- supprimer partitions > 90 jours
-- a programmer via un job planifie

cas 3: choisir un modele compact

# selection simple selon budget latence et memoire
candidats = [
  {"name": "model_large_fp32", "lat_ms": 60, "mem_mb": 1200, "acc": 0.92},
  {"name": "model_small_int8", "lat_ms": 18, "mem_mb": 180, "acc": 0.90}
]
def choix(cands, lat_budget=20, mem_budget=256):
    c = [m for m in cands if m["lat_ms"] <= lat_budget and m["mem_mb"] <= mem_budget]
    return max(c, key=lambda m: m["acc"]) if c else min(cands, key=lambda m: m["lat_ms"])

suivi et transparence

  • publier un tableau de bord “carbone et couts” par produit.
  • suivre delai de resolution des optimisations et gains reels observes.
  • partager les bonnes pratiques et les gabarits internes.
dashboard_kpis:
  top3_kwh_features: ["orders_dash", "clickhouse_backup", "daily_etl"]
  kwh_30d_total: 4200
  kgco2e_30d_total: 1260
  storage_cold_tib: 72
  egress_gb_30d: 3800

pieges et parades

  • optimisation micro -> faible gain -> choisir top 3 postes, mesurer avant/apres.
  • absence de mesures -> slogans -> instrumenter d’abord, arbitrer ensuite.
  • vues non rafraichies -> incoherence -> planifier refresh et invalider caches.
  • jobs orphelins -> cout caché -> etiqueter, desactiver par defaut, revue mensuelle.
  • precisions excessives -> IO inutiles -> agreger au besoin, adapter la resolution.

faq

  • Comment estimer l’empreinte carbone si je n’ai que des kWh ? Multipliez les kWh par un facteur carbone de votre region (gCO2e/kWh). Conservez la methode et les facteurs utilises dans vos rapports.

  • Quelles optimisations donnent souvent les meilleurs gains ? Passer en formats columnaires avec compression, materialiser les agregats critiques, et supprimer les exports repetitifs sont des gains rapides.

  • Faut il viser une precision de mesure parfaite avant d’agir ? Non. Une metrologie simple suffit pour identifier les gros postes. Affinez ensuite.

  • Comment eviter de casser la valeur metier en optimisant ? Demandez une preuve de valeur. Si la precision temps reel n’apporte pas de benefice mesure, revenez a une frequence plus sobre.

  • Qui doit piloter ces sujets ? Chaque equipe produit pour son perimetre, avec un support central finops/sobriete qui fournit schemas, outils et benchmarks internes.