InfluxDB: il database time series per IoT e monitoring

Riassunto

InfluxDB è il database pensato per sensori, metriche e serie temporali. In questa guida vedi il modello dati, l’errore classico tra tag e field, la scrittura in batch da Python, le query in Flux e SQL, retention e downsampling, la scelta tra InfluxDB 2.x e 3 e tre casi d’uso reali dal Politecnico di Torino.

Avatar Alessandro Fiori

PhD · Politecnico di Torino

In sintesi. Sensori, metriche di sistema, parametri vitali, prezzi: quando ogni dato ha un timestamp e ne arrivano migliaia al minuto, un database relazionale inizia a soffrire. InfluxDB è progettato per questo carico. In questa guida vedi il modello dati (bucket, measurement, tag, field), l’errore di schema più comune, come scrivere in batch da Python, come interrogare con Flux e SQL, come gestire retention e downsampling e quale versione scegliere nel 2026, con tre casi d’uso reali dal corso del Politecnico di Torino.

Un consorzio di aziende agricole del Monferrato installa sensori nei vigneti: temperatura dell’aria e del suolo, umidità del terreno, precipitazioni, irraggiamento, velocità del vento. Ogni sensore invia una lettura ogni 15 minuti. Con 8 appezzamenti e 6 grandezze misurate sembra poca roba — circa 4.600 righe al giorno. Poi il consorzio cresce a 60 aziende, i sensori passano a una lettura al minuto, e qualcuno chiede di confrontare la stagione corrente con le ultime cinque. Ci si ritrova con centinaia di migliaia di righe al giorno e query che scandiscono anni di storico.

È il momento in cui chi ha messo tutto in una tabella PostgreSQL scopre che le query rallentano, gli indici crescono più dei dati e cancellare i dati vecchi blocca la tabella. Non è colpa di PostgreSQL: è un pattern di accesso per cui non è stato progettato. Nella serie Stack Digitale 2026 abbiamo già visto come Kafka trasporta questi flussi e come Grafana li visualizza. Oggi guardiamo lo strato in mezzo: dove i dati vivono.

Cos’è una serie temporale e perché merita un database dedicato

Una serie temporale è una sequenza di misurazioni ordinate nel tempo: la temperatura di una stazione ogni 5 minuti, il battito cardiaco di un paziente ogni 30 secondi, l’uso della CPU di un server ogni 10 secondi. Il dato in sé è semplice. Quello che lo rende speciale è il modo in cui viene scritto e letto.

Il pattern di accesso delle time series

  • Scritture continue e quasi solo in append. I dati arrivano sempre “al presente”; raramente si aggiorna una lettura passata.
  • Letture per intervalli di tempo. Nessuno chiede “la riga con id 48.213.977”: si chiede “la media oraria di PM10 a Torino nell’ultima settimana”.
  • Aggregazioni onnipresenti. Medie, massimi, percentili, medie mobili su finestre temporali.
  • Il valore del dato decade. La lettura al secondo di ieri è preziosa; quella di tre anni fa serve solo come media oraria o giornaliera.

Perché un database row-oriented degrada

Un database relazionale classico memorizza i dati riga per riga e mantiene indici B-tree per trovarle. Con carichi da centinaia di migliaia di inserimenti al giorno succedono tre cose: gli indici su (sensor_id, timestamp) crescono e diventano costosi da aggiornare; le aggregazioni devono leggere righe intere anche se servono solo due colonne; la cancellazione dei dati vecchi con DELETE genera frammentazione e lavoro di vacuum. Il deck del corso lo riassume in una riga della tabella “scelta sbagliata / scelta giusta”: scritture time series a un milione al secondo → database row-oriented (sbagliato) → TimescaleDB o InfluxDB (giusto).

Per correttezza: TimescaleDB, un’estensione di PostgreSQL, è un’alternativa eccellente se il tuo team vive già in SQL e vuoi fare JOIN tra serie temporali e tabelle relazionali. InfluxDB ha senso quando le serie temporali sono il cuore del sistema e vuoi un ecosistema nato per quello: agente di raccolta (Telegraf), retention automatica, integrazione nativa con Grafana.

Il modello dati: bucket, measurement, tag e field

Prima di scrivere una riga di codice devi capire quattro concetti. Sbagliarli è la causa numero uno dei problemi di performance.

Modello dati InfluxDB: bucket, measurement, tag indicizzati, field e timestamp in una riga di line protocol
Anatomia di un punto InfluxDB: i tag descrivono chi ha misurato, i field cosa è stato misurato
  • Bucket (in InfluxDB 3 si chiama database): il contenitore, con una propria politica di retention. Es. agro.
  • Measurement: l’equivalente di una tabella. Es. sensors.
  • Tag: coppie chiave-valore indicizzate che descrivono la sorgente. Es. farm=cascina_rossa, plot=P03, sensor_type=soil.
  • Field: i valori misurati, non indicizzati. Es. soil_temp=8.4, soil_moisture=31.2.

Ogni punto ha poi il suo timestamp. In line protocol, il formato testuale con cui InfluxDB riceve i dati, un punto appare così:

sensors,farm=cascina_rossa,plot=P03,sensor_type=soil soil_temp=8.4,soil_moisture=31.2 1753084800000000000

Tag o field? L’errore classico

La regola è semplice: se ci filtri o raggruppi, è un tag; se ci fai calcoli, è un field. L’errore che vedo ogni anno nei progetti degli studenti è mettere tutto come field, compreso station_id. Il risultato funziona, ma ogni query “dammi i dati della stazione X” deve scandire tutti i punti del periodo invece di usare l’indice. L’errore opposto è altrettanto grave: usare come tag valori quasi unici, come un timestamp grezzo, un UUID di richiesta o l’ID di una sessione utente.

Cardinalità: il concetto che decide le performance

La series cardinality è il numero di combinazioni uniche di measurement + tag. Con 50 stazioni, 8 città e 3 tipi di sensore hai al massimo 1.200 serie: nessun problema. Se aggiungi un tag reading_id univoco per ogni lettura, la cardinalità esplode a milioni e l’indice in memoria di InfluxDB 2.x ne soffre pesantemente. InfluxDB 3 gestisce cardinalità molto più alte grazie al nuovo motore colonnare, ma la regola di buon senso resta valida: i tag descrivono entità stabili, non eventi singoli.

InfluxDB 2.x o InfluxDB 3: quale scegliere nel 2026

Questa è la domanda che i tutorial più vecchi non affrontano, e cambia parecchie cose. InfluxDB 3 è una riscrittura completa in Rust, basata su Apache Arrow, DataFusion e Parquet. Le differenze pratiche che contano:

AspettoInfluxDB OSS 2.xInfluxDB 3 Core
Linguaggi di queryFlux, InfluxQLSQL, InfluxQL (niente Flux)
LicenzaMITMIT / Apache 2.0
Interfaccia webIntegrata (Data Explorer, dashboard)Separata (InfluxDB 3 Explorer) o Grafana
Cardinalità elevataPunto deboleGestita bene (motore colonnare)
Automazioni interneTask in FluxProcessing Engine con plugin Python
DeploymentSingle nodeSingle node (HA e cluster nella versione Enterprise)

Il punto chiave: Flux è in modalità manutenzione. InfluxData continua a supportarlo con patch di sicurezza e correzioni critiche su 1.x e 2.x, ma non sviluppa nuove funzionalità e raccomanda SQL o InfluxQL per il codice nuovo. Il mio consiglio: se hai già un sistema su 2.x con Flux, non c’è urgenza di migrare. Se parti oggi con un progetto nuovo, valuta seriamente InfluxDB 3 con SQL: è la direzione del prodotto e SQL è una competenza che il tuo team riutilizza ovunque. Per la didattica, la 2.x resta comoda grazie all’interfaccia web integrata, ed è quella usata nei progetti del corso.

Scrivere i dati: dal sensore al bucket

La libreria ufficiale influxdb-client per Python (per InfluxDB 2.x) permette di scrivere punti in modo sincrono o in batch. La differenza è enorme: con la scrittura sincrona ogni punto è una richiesta HTTP; in batch il client accumula i punti e li invia a blocchi. Nelle note tecniche del progetto Smart City lo indichiamo esplicitamente: il consumer Kafka → InfluxDB deve usare la modalità batch.

from influxdb_client import InfluxDBClient, Point, WriteOptions

client = InfluxDBClient(url="http://localhost:8086", token="***", org="flowygo")
write_api = client.write_api(write_options=WriteOptions(
    batch_size=5_000, flush_interval=10_000  # 5.000 punti o 10 secondi
))

def on_reading(r):  # chiamata per ogni messaggio Kafka
    p = (Point("sensors")
         .tag("farm", r["farm"]).tag("plot", r["plot"]).tag("sensor_type", r["type"])
         .field("soil_temp", float(r["soil_temp"]))
         .field("soil_moisture", float(r["soil_moisture"]))
         .time(r["timestamp"]))
    write_api.write(bucket="agro", record=p)

# a fine lavoro: write_api.close() svuota il buffer

Nota il float() esplicito: se il primo valore scritto in un field è un intero e il successivo è un decimale, InfluxDB rifiuta il secondo per conflitto di tipo. È un errore frequente quando si importano CSV. Per l’import iniziale di milioni di righe storiche, poi, usa sempre la scrittura bulk (o influx write da riga di comando), mai inserimenti singoli.

Consulenza database

Il tuo database rallenta o non regge più la crescita? Partiamo dal modello dati e troviamo la soluzione giusta, relazionale o NoSQL.

Scopri la consulenza database →

Se usi InfluxDB 3, il client Python si chiama influxdb3-python e accetta lo stesso line protocol; il modello concettuale è identico.

Interrogare i dati: Flux e SQL a confronto

Vediamo la stessa domanda — media oraria della temperatura del suolo per appezzamento negli ultimi 7 giorni — nei due linguaggi.

Flux (InfluxDB 2.x)

from(bucket: "agro")
  |> range(start: -7d)
  |> filter(fn: (r) => r._measurement == "sensors" and r._field == "soil_temp")
  |> group(columns: ["plot"])
  |> aggregateWindow(every: 1h, fn: mean, createEmpty: false)

Flux è un linguaggio a pipeline: ogni |> passa il risultato al passo successivo. È potente — nel progetto P3 lo usiamo per calcolare i Growing Degree Days, cioè l’accumulo termico stagionale che determina lo sviluppo della vite, con una semplice map() che somma i gradi sopra i 10 °C — ma ha una curva di apprendimento propria.

SQL (InfluxDB 3)

SELECT
  plot,
  date_bin(INTERVAL '1 hour', time) AS hour,
  avg(soil_temp) AS avg_soil_temp
FROM sensors
WHERE time >= now() - INTERVAL '7 days'
GROUP BY plot, hour
ORDER BY hour;

Se conosci SQL, la leggi al primo colpo. La funzione date_bin() fa lo stesso lavoro di aggregateWindow(): raggruppa i punti in intervalli regolari. Se vuoi ripassare le basi di SELECT e GROUP BY, sul blog trovi una guida dedicata a SQL.

Data Explorer di InfluxDB Cloud con query SQL e grafico della temperatura media oraria del suolo per appezzamento
La query SQL di questo articolo nel Data Explorer di InfluxDB Cloud: temperatura del suolo di cinque appezzamenti, media oraria su 7 giorni

Retention e downsampling: tenere solo i dati che servono

Conservare per sempre una lettura ogni 30 secondi è costoso e inutile. La strategia standard ha due livelli, ed è quella richiesta nel progetto P7 di monitoraggio sanitario: un bucket vitals_raw con retention di 7 giorni per i parametri vitali ad alta frequenza, e un bucket vitals_hourly con retention di 5 anni che contiene solo le medie orarie. In InfluxDB 2.x la migrazione tra i due livelli è un task Flux pianificato:

option task = {name: "downsample_vitals", every: 1h}

from(bucket: "vitals_raw")
  |> range(start: -task.every)
  |> filter(fn: (r) => r._measurement == "vitals")
  |> aggregateWindow(every: 1h, fn: mean)
  |> to(bucket: "vitals_hourly")

Il database scarta automaticamente i dati scaduti, senza DELETE manuali e senza bloccare nulla. In InfluxDB 3 lo stesso risultato si ottiene con la retention per database e un plugin Python del Processing Engine pianificato.

InfluxDB nella pipeline: Kafka, Telegraf e Grafana

InfluxDB raramente lavora da solo. Nello stack che stiamo costruendo in questa serie occupa una posizione precisa: riceve i dati dallo streaming e li serve alla visualizzazione.

Pipeline IoT: sensori verso Kafka, consumer o Telegraf verso InfluxDB, poi Grafana con alert e dashboard
Dove si colloca InfluxDB: tra lo streaming di Kafka e le dashboard di Grafana
  • Kafka → InfluxDB. Due strade: un consumer Python con scrittura batch, come quello sopra, oppure Telegraf con il plugin kafka_consumer, che non richiede codice. Come abbiamo visto nella prima parte dedicata a Kafka, il vantaggio del log è che se InfluxDB è in manutenzione i dati aspettano nel topic e nessuna lettura va persa.
  • InfluxDB → Grafana. Grafana ha un datasource InfluxDB nativo che supporta Flux, InfluxQL e SQL. Attenzione alla configurazione: con la 2.x devi selezionare esplicitamente Flux come linguaggio, altrimenti Grafana si aspetta InfluxQL e le query falliscono. Nelle istruzioni del progetto P1 chiediamo di documentarlo nel README proprio perché è l’intoppo più comune.
  • Alert. Soglie critiche — PM10 sopra i limiti OMS, temperatura del suolo sotto i 2 °C, saturazione sotto il 94% — si configurano in Grafana Alerting sopra le query InfluxDB.

Tre casi d’uso reali dal corso

Tre dei sette progetti finali del corso di Advanced Data Modeling usano InfluxDB, ognuno con una sfida diversa:

ProgettoCosa misuraTagSfida principale
P1 Smart CityPM10, PM2.5, NO₂, O₃, temperatura, rumore ogni 5 min da 50 stazioni ARPA (~5 milioni di punti/anno)station_id, city, sensor_typeIngest in streaming da Kafka con scrittura batch
P3 Smart AgricultureTemperatura aria/suolo, umidità, pioggia, irraggiamento ogni 15 minfarm, plot, sensorGrowing Degree Days in Flux e alert gelate
P7 Sanità regionaleBattito, SpO₂, pressione, frequenza respiratoria ogni 30 s (dati sintetici)ward, bed, patient_pseudo_idRetention differenziata e alert clinici

Nel P7, con 10 pazienti, 4 parametri, una lettura ogni 30 secondi per 7 giorni, si arriva a circa 80.000 punti: un volume che InfluxDB gestisce senza alcuno sforzo, ma che basta per far capire agli studenti perché la retention va progettata prima, non dopo.

Best practice da applicare fin dal primo giorno

  • Progetta i tag prima di scrivere il primo punto. Chiediti: per cosa filtrerò? Quelli sono i tag. Tutto il resto è field.
  • Evita tag ad alta cardinalità: timestamp, UUID, ID di richiesta, testo libero.
  • Scrivi sempre in batch, sia in streaming sia negli import storici.
  • Fissa il tipo dei field (float sempre float) per evitare conflitti.
  • Usa la precisione giusta del timestamp: i secondi bastano per un sensore ambientale, non serve il nanosecondo.
  • Definisci retention e downsampling in fase di progetto, non quando il disco è pieno.
  • Nuovo progetto nel 2026? Scrivi le query in SQL o InfluxQL, non in Flux.

Il prossimo passo: orchestrare tutto

Adesso hai i tre pezzi dello strato dati in tempo reale: Kafka trasporta, InfluxDB conserva, Grafana mostra. Manca una cosa: chi coordina i processi batch — l’import notturno dei CSV, il ricalcolo dei KPI, la sincronizzazione con PostgreSQL? Nel prossimo articolo della serie parliamo di Apache Airflow e costruiamo la pipeline completa del progetto Smart Agriculture.

Se nella tua azienda ci sono sensori, macchinari o sistemi che producono dati temporali e non sai ancora dove metterli — o li hai messi in un foglio Excel che ormai non si apre più — richiedi un audit dati e AI. Partiamo dal volume reale dei tuoi dati e scegliamo lo strumento proporzionato, che sia InfluxDB, TimescaleDB o qualcosa di ancora più semplice.