Progettare un’applicazione che non debba gestire dati di alcun tipo è possibile, ma sicuramente è una mosca bianca nel panorama degli scenari possibili.
Ma dover gestire dei dati non significa necessariamente dover utilizzare un’unica tipologia di prodotto: vediamo quindi come scegliere lo strumento più adatto per i nostri scopi.
Se il nostro modello dati è formato da dati incasellabili in entità e attributi (tabelle e campi), se dobbiamo creare relazioni fra varie entità, creare resoconti e, soprattutto, se i nostri dati hanno più o meno sempre la stessa struttura, la scelta più indicata sarà quasi certamente un database relazionale.
Sono presenti sul mercato da decenni, ne esistono di ogni tipo: pesi piuma come SQLite, giganti come Oracle e Informix, campioni di popolarità come PostgreSQL e MySQL, formidabili tuttofare come FileMaker.
Al di là delle differenze più o meno marcate, i prodotti di questo tipo condividono un importante aspetto di base: sono pensati per gestire dati archiviati in tabelle e uniti fra loro tramite relazioni, che sotto certi aspetti ricordano i precetti dell’insiemistica.
Chi ha una certa età ricorderà le lezioni a base di mele (“queste sono tutte le mele del cesto”), alunni (“questi sono gli alunni della III C”) e insiemi (“queste sono le mele di Paolo, della III B”).
Si tratta di strumenti da utilizzare su strutture dati molto semplici ma che richiedono tempi di lettura velocissimi. Solitamente i dati vengono salvati in memoria, rendendo l’accesso istantaneo. Per contro, la quantità di dati memorizzabile è limitata e la struttura è, come indica il nome, composta da coppie chiave-valore, dove l’univocità è garantita dalla chiave.
Un esempio molto famoso è Redis, utilizzato per il caching di Twitter e Pinterest.
Se una semplice raccolta di chiavi-valori è troppo limitata per i nostri scopi, possiamo prendere in considerazione un database distribuito di tipo wide-column.
Questo genere di database è di tipo NoSQL, quindi non utilizza SQL come linguaggio di interrogazione e non ha la possibilità di creare relazioni.
Però consente di associare a una chiave più di una colonna, ma senza i vincoli imposti dai database relazionali: qui la struttura dei dati può variare perché non deve essere predeterminata. Avremo quindi un record composto da 5 colonne e uno da 2, magari completamente diverse.
Strumenti di questo tipo, come Apache Cassandra, consentono di scalare in maniera molto efficiente e per questo motivo sono utilizzati in applicazioni che devono gestire grandi quantità di dati storicizzati, con molte operazioni di scrittura e relativamente poche letture.
Ad esempio Netflix utilizza Cassandra per gestire i dati degli abbonati.
Sempre all’interno di questa tipologia possiamo far rientrare la categoria dei database orientati ai documenti.
Un documento racchiude al proprio interno una serie di coppie chiave-valore le quali - come nel caso precedente - non devono seguire uno schema predeterminato.
I documenti, a loro volta, sono riuniti in collection che possono avere una gerarchia che in qualche modo può ricordare una struttura relazionale molto basilare.
In questo modo si possono recuperare dati accomunati da questa gerarchia, ma non disponendo di relazioni fra le varie collection, non è possibile utilizzare delle JOIN nelle interrogazioni.
Per questo motivo e proprio perché i documenti sono privi di schema, l’approccio nei confronti dei nostri dati seguirà un cammino opposto a quello richiesto da un database relazionale: mentre quest’ultimo lavora in maniera efficiente se i dati vengono normalizzati, riducendoli alle loro componenti più piccole, uno strumento come Dynamo o MongoDB richiederà di accorpare i dati all’interno di un unico documento.
Questo genere di prodotti è la scelta ideale se non sappiamo con precisione come devono essere strutturati i nostri dati, se è più importante scrivere i dati piuttosto che leggerli o aggiornarli di frequente e, soprattutto, se i dati non devono essere messi in relazione tra loro.
Se i nostri dati non sono particolarmente strutturati ma devono poter essere messi in relazione in maniera complessa e trasversale, ricondurre il nostro modello dati a una struttura relazionale standard può rivelarsi complesso e poco efficiente.
In casi del genere può valere la pena prendere in considerazione un Graph db, come Neo4j, utilizzato da Internet Movie Database.
Solitamente, quando visualizziamo una scheda su IMDB, chiediamo al sistema di presentarci una serie piuttosto articolata di informazioni: la scheda di un film, con la sinossi tradotta in italiano, se disponibile; i personaggi e interpreti, i quali avranno il rimando alla scheda dell’attore; il cast completo, le foto e via dicendo.
Se decidiamo di selezionare un attore, invece, il sistema presenterà la biografia, i film a cui ha preso parte in veste di attore, eventuali altre partecipazioni artistiche in qualità di doppiatore, autore, regista e tanto altro.
Il problema di presentare le informazioni di questo tipo con un database relazionale è che una relazione classica si limita ad informarci che il soggetto A (es. “Cary Grant”) è collegato al soggetto B (“Intrigo internazionale”). Ma per indicare il ruolo ricoperto da Cary nel capolavoro di Hitchcock dobbiamo impostare una tabella di Join per gestire la relazione molti a molti, incorporando degli attributi nella Join per definire i ruoli e, una volta completata la fase di modellazione dati, dovremo realizzare un sistema di interrogazione piuttosto complesso.
Per capire meglio, prendiamo un esempio mutuato direttamente dal sito di Neo4j.
Partendo da un modello dati che, in uno schema relazionale avrebbe questa struttura:
Tutto sommato una struttura molto semplice.
Eppure, se vogliamo ottenere un elenco di tutti i dipendenti che fanno parte del dipartimento “IT”, dovremo utilizzare una query SQL come questa:
La versione “graph” di questa query, utilizzando Cypher come linguaggio di interrogazione da confrontare con SQL sarebbe:
Molto più semplice, anche operando su una struttura dati così poco elaborata.
Come vediamo, per rendere la gestione delle relazioni più efficiente i Graph DB utilizzano delle relazioni (connections o edges) che sono identificate da etichette (labels).
Tornando al nostro esempio cinéphile, l’entità “ATTORI” potrà essere collegata a “FILM” tramite una relazione qualificata come “recita_in”. In questo modo l’utilizzo delle tabelle di Join può essere evitato, a tutto vantaggio della flessibilità e della velocità di interrogazione: se la struttura del nostro sistema prevede decine di Join e le prestazioni non sono brillanti, adottare un Graph DB può essere la soluzione.
In alcune situazioni più essere conveniente limitarsi a creare un contenitore dove le varie sorgenti andranno a salvare dati non strutturato o, nel caso dei data lake, addirittura grezzo.
Ed è proprio la differenza nel livello di “preorganizzazione” di questi dati grezzi a costituire la principale differenza tra un data warehouse e un data lake.
Un data warehouse è un database che unisce più sorgenti dati, solitamente database relazionali, li ottimizza e li arricchisce in modo da facilitare l’analisi e la creazione dei resoconti.
Un data lake, invece, può contenere dati provenienti da db relazionali e non, file di testo provenienti da dispositivi IoT, letture analitiche da siti web e social media e via dicendo. Questo crogiolo di informazioni non ha dunque una struttura predefinita: la struttura viene creata dallo strumento chiamato ad interpretare questi dati in tempo reale.
Questi dati avranno quindi il solo requisito di essere in un formato leggibile e dovranno risiedere in una collocazione centralizzata, un repository. Qui un’applicazione che potrà utilizzare logiche di machine learning o strumenti di analisi andrà ad esaminare questi dati e ne estrapolerà dei resoconti organizzati.
Pensiamo ad esempio a dati raccolti da sensori, informazioni provenienti dai social media o da file di log: poca struttura a fronte di un’enorme quantità in questi dati che, analizzati dallo strumento giusto possono fornire indicazioni di grande utilità.
Concludiamo questa carrellata con una famiglia di prodotti che ricorda da vicino la filosofia che sta alla base dei motori di ricerca che utilizziamo ogni giorno.
Un motore di ricerca “full text”, come ElasticSearch, utilizza un sistema molto sofisticato per creare indici intelligenti di ogni oggetto memorizzato (solitamente la struttura dati è di ricalca i NoSQL orientati ai documenti) e, per questo motivo, riesce a restituire facilmente risultati di una ricerca su diversi “campi”, spesso includendo indicazioni quali percentuali di accuratezza sui quali potremo costruire un ordinamento in modo da presentare per primi i risultati più pertinenti.
Come abbiamo visto, oggi sul mercato esiste un vasto catalogo di prodotti che sono simili solo in apparenza. Tuttavia, se andiamo appena oltre il concetto di “sistema di archiviazione dati” vediamo che le differenze sono spesso molto marcate e, di conseguenza, diventa necessario scegliere a ragion veduta.
E il modo migliore per compiere questa scelta è di valutare con attenzione qual è l’uso che dovremo fare dei nostri dati.