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.

- 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 1753084800000000000Tag 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:
| Aspetto | InfluxDB OSS 2.x | InfluxDB 3 Core |
|---|---|---|
| Linguaggi di query | Flux, InfluxQL | SQL, InfluxQL (niente Flux) |
| Licenza | MIT | MIT / Apache 2.0 |
| Interfaccia web | Integrata (Data Explorer, dashboard) | Separata (InfluxDB 3 Explorer) o Grafana |
| Cardinalità elevata | Punto debole | Gestita bene (motore colonnare) |
| Automazioni interne | Task in Flux | Processing Engine con plugin Python |
| Deployment | Single node | Single 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 bufferNota 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.
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.

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.

- 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:
| Progetto | Cosa misura | Tag | Sfida principale |
|---|---|---|---|
| P1 Smart City | PM10, PM2.5, NO₂, O₃, temperatura, rumore ogni 5 min da 50 stazioni ARPA (~5 milioni di punti/anno) | station_id, city, sensor_type | Ingest in streaming da Kafka con scrittura batch |
| P3 Smart Agriculture | Temperatura aria/suolo, umidità, pioggia, irraggiamento ogni 15 min | farm, plot, sensor | Growing Degree Days in Flux e alert gelate |
| P7 Sanità regionale | Battito, SpO₂, pressione, frequenza respiratoria ogni 30 s (dati sintetici) | ward, bed, patient_pseudo_id | Retention 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.
