Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica
Elaborato finale in Basi di Dati
MongoDB: caratteristiche
Anno Accademico 2014/2015
Candidato:
Domenico Cerbone
matr. N46000954
Ringrazio la mia famiglia e i
miei amici, per aver condiviso
questi anni con me in ogni
momento.
Grazie,di cuore.
Indice
Indice .............................................................................................................................................III
Introduzione......................................................................................................................................5
Capitolo 1: NoSql e Orientazione ai Documenti..............................................................................6
1.1 NoSql......................................................................................................................................6
1.1.1 Differenze NoSql vs Sql................................................................................... ...........7
1.2 Teorema CAP.......................................................................................................................10
1.3 Orientazione ai Documenti...................................................................................................12
Capitolo 2: Caratteristiche .............................................................................................................16
2.1 Query Ad Hoc.......................................................................................................................17
2.1.1 Query Find()....................................................................................................
2.1.2 Query Insert()..................................................................................................
2.1.3 Query Update()................................................................................................
2.1.4 Query Remove()..............................................................................................
.........18
.........18
.........19
.........19
2.2 Aggregazione........................................................................................................................20
2.3 Indicizzazione.......................................................................................................................23
2.4 Replicazione.........................................................................................................................25
2.5 Sharding................................................................................................................................29
Capitolo 3: Gestione e Amministrazione di MongoDB.................................................................33
3.1 Shell Mongo.........................................................................................................................34
3.2 Gestione della Memoria.......................................................................................................34
Conclusioni.....................................................................................................................................37
Bibliografia.....................................................................................................................................38
Introduzione
MongoDB (da "humongous" che significa enorme) è un database Non Relazionale,
orientato ai documenti, scritto in C++ e open source. [1]
Sviluppato inizialmente dalla 10GEN (società di software) nell'ottobre 2007, il database ha
visto la prima release nel febbraio 2009 fino all'ultima risalente all'aprile 2014 con la
versione 2.6.0.
MongoDB è rilasciato sotto licenza GNU AGPL("Affero General Public License") v.3,
mentre i driver sono rilasciati sotto licenza Apache 2.0.
Classificato come un database di tipo NoSql, Mongo si allontana dalla struttura
tradizionale basata su tabelle dei database relazionali in favore di documenti in stile JSON
("JavaScript Object Notation") con schema dinamico (Mongo chiama il formato BSON,
ovvero "Binary JSON"), rendendo l'integrazione dei dati di alcuni tipi di applicazioni più
facile e veloce.
Le operazioni sul database vengono eseguite in console tramite JavaScript e questo
permette un uso immediato per molti utenti, senza la necessità di imparare particolari
sintassi o linguaggi.
Mongo è stato progettato per essere:
•
potente, flessibile e scalabile
•
per combinare le migliori caratteristiche dei database key-value
•
semplice nell'uso
Attualmente esso è adottato in svariati ambiti di utilizzo come:
•
Archiviazione e registrazione di eventi
•
Sistemi di gestione di documenti e contenuti
•
E-commerce
•
Infrastrutture del lato server di sistemi mobili
5
Capitolo 1: NoSql e Orientazione ai Documenti
I database orientati ai documenti nascono per gestire informazioni eterogenee in un
ambiente distribuito e le principali differenze tra le varie implementazioni si basano sul
grado di concorrenza fornite.
Con il termine document si intende un insieme di dati concettualmente raggruppati che
possono essere codificati in vari formati, come XML, JSON, BSON e altri tipi binari.
Una prima differenza rispetto ai database relazionali è data dalla mancanza di pianificazione
nella struttura dei dati; infatti ogni document, anche all'interno della stessa collezione, può
avere attributi differenti per numero e tipo (approccio schema-less).
Come la maggioranza dei database NoSql, i sistemi orientati ai documenti non supportano
le proprietà ACID ( Atomicity, Consistency, Isolation, Durability) delle transazioni, mentre
il linguaggio di interrogazione è generalmente più dettagliato e permette di fare query su
range di attributi, così come operazioni di aggregazioni complesse.
MongoDB è stato progettato con un approccio non relazionale per essere scalabile
orizzontalmente su più macchinari e per memorizzare ordini di grandezza di dati maggiori
rispetto al passato.
1.1 NoSql
Abbiamo finora letto di parole e concetti risalenti al termine NoSql.
Ma che cosa è NoSql? [2]
NoSql è un movimento che promuove sistemi software dove la persistenza dei dati è
caratterizzata dal fatto di non utilizzare il modello relazionale, di solito usato dai database
tradizionali (RDBMS).
L'espressione, che sta per "Not Only Sql", fu usato per la prima volta nel 1998 per una base
di dati relazionale open source che non usava un'interfaccia Sql.
Il termine fu reintrodotto nel 2009 da Eric Evans come tentativo per descrivere l'emergere
6
di un numero consistente di sistemi di archiviazione dati distribuiti non relazionali che
spesso non tentano di fornire le classiche garanzie ACID.
I sistemi NoSql hanno tali caratteristiche:
•
non richiedono uno schema fisso (schemaless)
•
evitano operazioni di unione( Join)
•
scalano orizzontalmente per rendere più semplici le operazioni di
lettura/scrittura
•
replicare e distribuire dati su più server
•
uso efficiente di indici distribuiti e RAM per la memorizzazione dei dati
•
aggiunge dinamicamente nuovi attribuiti ai record di dati
1.1.1 Differenze NoSql vs Sql
I database non relazionali o NoSql hanno ricevuto un forte impulso di sviluppo, tanto da
diventare quasi una scelta obbligata in determinati scenari d’uso, contrapponendosi ai
database relazionali o SQL. Vediamo cosa differenzia un database relazionale da uno non
relazionale [3]:
•
I database relazionali, come dice il termine stesso, si basano su relazioni univoche
fra i dati. In pratica, tutti i dati da trattare sono memorizzati in strutture fisse, dette
tabelle, dove sono posti l’uno di seguito all’altro a formare singole record (ossia le
righe di ciascuna tabella). Ogni dato contenuto in una tabella si trasforma in
un’informazione vera e propria solo se associato al suo corrispondente attributo, che
rappresenta l’intestazione della colonna in cui il dato è memorizzato (Fig.1).
Figura1:
esempio Tabella Sql
7
Questa struttura obbliga la frammentazione delle informazioni fra differenti tabelle, anche
quando i dati descrivono un medesimo oggetto. Per identificare le informazioni
appartenenti a uno stesso oggetto si utilizzano tutta una serie di operazioni logiche come il
JOIN, che basano i propri calcoli sulle chiavi esterne o foreign keys che mettono in
collegamento una tabella all’altra.
Questa peculiarità impone ai sistemi SQL di svolgere continue interrogazioni, in quanto i
dati devono essere raccolti e combinati fra loro da differenti tabelle, anche per operazioni
semplici, come l’inserimento, la cancellazione e l’aggiornamento. Questa forte
frammentazione prescrive anche un rigido controllo sulle relazioni e sulla validità dei dati
residenti nelle differenti tabelle in modo da preservare l’integrità del database, a scapito
della flessibilità. È anche per questo motivo che i database relazionali si basano su schemi
tabellari (definiti schemi entità-relazione) predefiniti a monte e che difficilmente possono
essere riadattati a nuove situazioni, a meno di non correre il rischio di una corruzione dei
dati.
•
I database non relazionali sono nettamente differenti da quelli SQL. I dati sono
conservati in documenti (Fig.2), non in tabelle, per cui la prima differenza con i DB
relazionali sta nel fatto che le informazioni non sono distribuite in differenti
strutture logiche, ma vengono aggregate per oggetto in documenti la cui natura può
essere di tipo Key-Value (che rappresenta la forma primitiva di database NoSql) o
Document Store basati su semantica JSON. Ogni documento aggregato raccoglie
tutti i dati associati a un’entità, in modo che qualsiasi applicazione possa trattare
l’entità come oggetto e valutare in un sol colpo tutte le informazioni a essa
correlate. In questo modo, si evitano anche i fardelli computazionali dovuti ai
passaggi di aggregazione delle informazioni tipici del linguaggio SQL, in quanto
tutti i dati necessari e corrispondenti a un medesimo oggetto sono già disponibili in
un unico documento. L’assenza di tabelle permette ai database non relazioni di
essere schemaless, ossia privi di un qualsiasi schema definito a priori e questa
caratteristica conferisce ai DB NoSql un altro vantaggio non trascurabile.
8
Figura2:
esempio Documento NoSql
Dalle caratteristiche citate si intravedono i vantaggi di una soluzione NoSql rispetto a una
SQL, che possiamo così elencare:
1. Leggerezza computazionale: i database NoSql non prevedono operazioni di
aggregazione sui dati, in quanto tutte le informazioni sono già raccolte in un unico
documento associato all’oggetto da trattare. Negli ambienti SQL la complessità di
queste operazioni, e quindi il peso computazionale, cresce con l’ingigantirsi della
base di dati, del numero di tabelle e delle informazioni da trattare. Il NoSql, invece,
non ha limiti di dimensioni in questo senso. Così si ottengono migliori prestazioni e
performance anche in ambienti di Big Data. Lo scotto da pagare a tutta questa
flessibilità e alla proprietà di aggregazione dei database NoSql è la duplicazione
delle informazioni. In realtà, i costi sempre meno proibitivi dei sistemi di storage
rendono questo svantaggio poco importante.
2. Assenza di schema: i database NoSql sono privi di schema in quanto il documento
JSON contiene tutti i campi necessari, senza necessità di definizione. In questo
modo, possiamo arricchire le nostre applicazioni di nuovi dati e informazioni,
definibili liberamente all’interno dei documenti JSON senza rischi per l’integrità
dei dati. I database non relazionali, a differenza di quelli SQL, si rivelano quindi
adatti a inglobare velocemente nuovi tipi di dati e a conservare dati semi strutturati
o non strutturati.
3. Scalabilità orizzontale garantita: l’aggregazione dei dati e l’assenza di uno
schema definito a priori offre l’opportunità di scalare orizzontalmente i database
NoSql senza difficoltà e senza rischi operativi.
9
1.2 Teorema CAP
Poiché nei database non relazionali non vengono rispettate le proprietà ACID, lo studioso
Eric Brewer nell’articolo “Towards robust distributed systems”[4] ha formalizzato il
cosiddetto Teorema CAP. [5]
Esso specifica come in un contesto distribuito si possano identificare 3 proprietà
fondamentali:
•
Consistency: i dati rimangono consistenti alla fine dell’esecuzione di un’operazione,
ovvero una volta effettuata un update tutti i client dispongono degli stessi dati.
•
Availability: ogni richiesta effettuata al database riceve una risposta su ciò che sia
riuscito o fallito.
•
Partition Tolerance: il sistema continua a funzionare anche qualora la
comunicazione tra server sia inaffidabile e si verifichino perdite di messaggi.
L’unico evento che possa causare l’impossibilità di comunicazione è la mancanza
globale di connettività (total network failure).
Uno studio compiuto da Seth Gilbert e Nancy Lynch afferma come sia possibile
soddisfare contemporaneamente solo due delle tre proprietà (Fig.3), ottenendo i seguenti
scenari:
•
Consistency+Availability (CA): tutti i nodi sono in contatto tra di loro. Quando
avviene una partizione della rete (le connessioni di rete tra due gruppi di sistemi
vengono a mancare contemporaneamente), il sistema si blocca.
•
Availability+Partition Tolerance (AP): il sistema è sempre disponibile, ma è
possibile che alcuni dati restituiti siano inaccurati.
•
Consistency+Partition Tolerance (CP): alcuni dati possono non essere accessibili,
ma i rimanenti sono considerati consistenti.
10
Figura 3:
Diagramma Teorema CAP
Le conseguenze del teorema non sono strettamente limitanti e focalizzarsi su due attributi
non esclude completamente il terzo, ma semplicemente non rende possibile garantirlo a
priori.
A fronte dei risultati offerti dal teorema CAP, i database di tipo NoSql, per poter essere
scalabili come richiesto, devono sacrificare delle proprietà e dunque non possono in alcun
modo aderire strettamente al modello ACID.
MongoDB identifica due scenari di applicazione del teorema CAP, secondo meccaniche di
sharding e/o di replicazione.
A livello di cluster basato su sharding, MongoDB offre:
•
Consistenza forte: un dato risiede su uno e un solo shard, pertanto non è possibile
ottenere valori inconsistenti.
•
Piena tolleranza alle partizioni: anche nell’eventualità si abbia una partizione
nella rete, le interrogazioni non ritornano dati incorretti. Gli shard continuano a
lavorare in modo indipendente.
•
Disponibilità dei dati in senso debole: letture e scritture su uno shard offline non
sono possibili e l’operazione non potrà terminare con successo.
11
A livello di replicazione dei dati si hanno le stesse proprietà, ma garantite secondo differenti
politiche:
•
Consistenza forte: come comportamento predefinito, tutte le letture sono gestite da
un singolo server, ovvero il nodo primario.
•
Piena tolleranza alle partizioni: se un numero sufficiente di nodi non sono più
raggiungibili, viene automaticamente eletto un nuovo server primario, garantendo
sempre la possibilità di ricezione delle richieste.
•
Disponibilità dei dati in senso debole: quando il server primario non è
raggiungibile non è possibile accedere ad alcun dato.
Bisogna comunque notare come MongoDB sia un caso particolare di database NoSql dove
non è possibile definire a priori il grado di fedeltà al teorema CAP. E’ possibile ottenere,
attraverso opportune configurazioni e richieste da parte dei client, una maggior disponibilità
dei dati a fronte di una minore consistenza degli stessi.
1.3 Orientazione ai Documenti
In MongoDB l’unità fondamentale di memorizzazione delle informazioni è rappresentata
dal documento. Tale termine non ha una definizione precisa e univoca, in quanto un
documento è un qualsiasi insieme di informazioni concettualmente coerenti raggruppate
secondo una data codifica che, nel caso di MongoDB, è il formato BSON.
Un documento BSON è concettualmente simile a un oggetto JSON codificato in forma
binaria, con alcune differenze quali il supporto alla memorizzazione delle date e
performance migliori in fase di lettura e scrittura (gli integer, per esempio, sono
memorizzati in formato binario, al contrario del formato JSON nel quale sono del
semplice testo e necessitano di conversione in formato numerico).
Un esempio di documento in MongoDB è raffigurato in fig.4
12
Figura 4:
esempio struttura documento
Il documento mydoc in esame permette di mostrare l’eterogeneità del formato
JSON/BSON, infatti all’interno dello stesso documento sono presenti integer a 64 bit
(campo “views”), array (campo “contribs”), date (campi “birth” e “deaths”), documenti
annidati (campo “name”) e tipi speciali come ObjectID (campo “_id”). Quest’ultimo campo
ha particolare importanza perché permette l’identificazione univoca del documento,
concetto simile a quello di chiave primaria in un database relazionale. Se non viene
esplicitato e dunque imposto manualmente per esigenze particolari, il campo_id viene
autonomamente valorizzato con un ObjectID, un tipo di dato del formato BSON che, oltre a
essere generato con criteri di unicità, dispone di una struttura informativa nelle sue
componenti.
Un ObjectID ha una struttura di dimensione pari a 12 byte così composta (Fig.5):
•
4 byte contenenti i secondi trascorsi dal 1° gennaio 1970 al momento attuale di
creazione (timestamp unix-like).
•
3 byte raffiguranti l'id della macchina di creazione.
•
2 byte raffiguranti l'id del processo di creazione.
•
3 byte di contatore incrementale.
Figura 5:
Struttura ObjectID
13
Una simile configurazione ha la proprietà che, in caso di inserimenti consecutivi, i record
vengano memorizzati per la maggioranza in ordine di inserimento. Mentre i primi 9 byte si
occupano di fornire informazioni sul sistema, gli ultimi 3 byte garantiscono l'unicità di
generazione, per un massimo di 16.777.216 documenti creati al secondo per singolo
processo.
I documenti vengono raccolti in collezioni (collection) le quali rappresentano una
suddivisione concettuale dei dati in modo analogo alle tabelle dei database relazionali; a
differenza di quest’ultime, in MongoDB una collezione non ha vincoli strutturali e
pertanto i documenti presenti all’interno possono avere campi diversi, per tipo e valore.
A loro volta, le collection risiedono nel database che rappresenta l’universo del sistema di
cui si vogliono gestire i dati. [6]
La mancanza di una struttura prestabilita nelle collection è una caratteristica piuttosto
diffusa nei sistemi NoSQL e rappresenta una prima, importante differenza rispetto ai
database relazionali. Tale approccio viene definito schema-less e permette ai sistemi basati
su MongoDB di cambiare agevolmente forma e modellare dati che cambiano nel tempo.
Bisogna però notare come MongoDB non permetta alcune operazioni fondamentali tipiche
dei database relazionali, quali join tra collection.
La mancanza di controlli di integrità referenziale tra i dati obbliga il progettista a
modellare lo schema logico con modalità molto diverse da quelle messe in atto durante lo
sviluppo di soluzioni SQL-based, per esempio attraverso accorpamento dei dati in
un’unica collection.
Un tipo particolare di collezioni sono le capped collection, strutture di dimensione
prestabilita che agiscono come buffer circolari. Una volta che si raggiungono le
dimensioni massime imposte precedentemente, i documenti più vecchi vengono
sovrascritti da quelli nuovi.
In fase di aggiunta di nuovi record viene garantita la memorizzazione in ordine di
inserimento, proprietà che permette di leggere i dati ordinati senza l’uso di indici (nel caso
di capped collection, se non viene esplicitato diversamente, il sistema non crea un indice
sul campo “_id”).
14
La mancanza dell’indice permette di eliminare l’overhead dato dalla gestione di
quest’ultimo, massimizzando così le prestazioni del sistema.
Le capped collection limitano le possibilità di manipolazione dei dati, infatti su di esse non
è possibile eliminare i singoli documenti e non sono permesse modifiche ai dati che
incrementino la dimensione dei documenti.
In virtù di queste caratteristiche, le capped collection acquisiscono particolare importanza
in scenari di logging dove sono richieste scritture rapide a fronte di un controllo debole sui
dati.
15
Capitolo 2: Caratteristiche
Analizziamo ora, introducendo e poi in dettaglio, le caratteristiche fondamentali di
MongoDB. [1]
Esse sono:
•
Query Ad Hoc: MongoDB supporta ricerche per campi, intervalli e regular
expression. Le query possono restituire campi specifici del documento e anche
includere funzioni definite dall'utente in JavaScript.
•
Aggregazione: MongoDB supporta due modalità di aggregazione dei dati: il
MapReduce e l'Aggregration Framework. Quest'ultimo lavora come una pipeline e
permette di ottenere risultati molto più rapidamente del MapReduce grazie
all'implementazione in C++.
•
Indicizzazione: Qualunque campo in MongoDB può essere indicizzato; sono
disponibili anche indici secondari, indici unici, indici sparsi, indici geospaziali e
indici full text.
•
Replicazione: MongoDB fornisce alta disponibilità e aumento del carico gestito
attraverso i replica set. Un replica set consiste in due o più copie dei dati. Ogni
replica può avere il ruolo di copia primaria o secondaria in qualunque momento. La
replica primaria effettua tutte le scritture e le letture. Le repliche secondarie
mantengono una copia dei dati della replica primaria attraverso un meccanismo di
replicazione incluso nel prodotto. Quando una replica primaria fallisce, il replica set
inizia automaticamente un processo di elezione per determinare quale replica
secondaria deve diventare primaria. Le copie secondarie possono anche effettuare
letture, con dati eventualmente consistenti di default.
•
Sharding: MongoDB scala orizzontalmente usando lo sharding. L'utente deve
scegliere una chiave di sharding, che determina come i dati di una collection saranno
distribuiti tra i vari nodi. I dati sono divisi in intervalli (basati sulla chiave di shard) e
distribuiti su molteplici shard.
16
2.1 Query Ad Hoc
In MongoDB una query si rivolge a una collezione specifica di documenti. [7]
Le query specificano i criteri o condizioni, che identificano i documenti che MongoDB
ritorna ai clienti.
Essa può includere una projection (proiezione) che specifica i campi dei documenti
corrispondenti da tornare.
Facoltativamente, è possibile modificare le query imponendo limiti, salti, e ordinamenti.
Le query presentano il seguente comportamento:
•
Tutte le query in MongoDB si riferiscono a una singola collezione.
•
È possibile modificare la query imponendo limits, skips, e sort orders.
•
L'ordine dei documenti restituiti da una query non è definito se non si specifica una
sort ().
•
Le operazioni che modificano documenti esistenti (ad esempio aggiornamenti)
utilizzano la stessa sintassi delle query che selezionano i documenti da aggiornare.
•
In aggregazione, il $match fornisce l'accesso alle query MongoDB.
Per ottimizzare le query MongoDB elabora le richieste e sceglie il piano di query più
efficiente per una query determinato dagli indici disponibili. Il sistema di query utilizza
quindi questo piano ogni volta che si esegue la query.
L'ottimizzazione delle query nelle cache è utile solo per le query che possono avere più di
un piano praticabile.
L'ottimizzazione rivaluta occasionalmente piani di query, come il contenuto delle
modifiche di raccolta per garantire piani di query ottimali.
È possibile utilizzare il metodo explain() per visualizzare le statistiche circa il piano di
query per una determinata query.
Questa informazione può aiutare come sviluppare strategie di indicizzazione.
Ci sono tre classi di operazioni di scrittura in MongoDB: insert, update, remove.
Operazioni di insert aggiungono nuovi dati a una raccolta; operazioni di update modificano
17
i dati esistenti, e remove cancella i dati da una collezione.
Nessun inserimento, aggiornamento o rimozione può influenzare più di un documento.
Per l'update e remove è possibile specificare criteri o condizioni, che identificano i
documenti da aggiornare o rimuovere.
Queste operazioni utilizzano la stessa sintassi di query per specificare i criteri operazioni di
lettura.
2.1.1 Query Find()
Per le operazioni di selezione, MongoDB fornisce un metodo db.collection.find().
Il metodo accetta sia i criteri di query che le projection, e restituisce un cursore per i
documenti corrispondenti.
Facoltativamente, è possibile modificare la query imponendo limits, skips, e sort orders.
Esempio:
Questa query seleziona i documenti della collezione users che corrispondono alla
condizione di età maggiore di 18; per specificare tale condizione, la query utilizza $gt.
La query restituisce al massimo 5 documenti corrispondenti (o più precisamente, un cursore
a tali documenti). I documenti corrispondenti torneranno solo con il _ID, nome e campi di
indirizzo.
MongoDB fornisce un metodo db.collection.findOne() come un caso particolare di find(),
il quale restituisce un unico documento.
2.1.2 Query Insert()
In MongoDB, il metodo db.collection.insert() aggiunge nuovi documenti a una collezione.
Esempio:
18
La seguente operazione inserisce un nuovo documento nella collezione users.
Il nuovo documento ha quattro campi: nome, età, stato e un campo_ID.
MongoDB aggiunge sempre il campo _id al nuovo documento se questo campo non esiste.
Se si aggiunge un nuovo documento senza il campo _ID, la libreria client mongod aggiunge
un campo _id e popola il campo con un ObjectId unico.
Se si specifica il campo _id, il valore deve essere univoco all'interno della collezione; se si
tenta di creare un documento con un valore _id duplicato, mongod restituisce un'eccezione
di chiave duplicata.
2.1.3 Query Update()
Il metodo db.collection.update() modifica documenti esistenti in una collezione.
Il metodo db.collection.update() può accettare criteri di query per determinare quali
documenti aggiornare.
Esempio:
Questa operazione di aggiornamento sulla raccolta users imposta il campo di stato di A per
i documenti che corrispondono ai criteri di età superiore a 18.
2.1.4 Query Remove()
il metodo db.collection.remove() cancella i documenti da una collezione.
Il metodo db.collection.remove() accetta un criterio di query per determinare quali
19
documenti rimuovere.
Esempio:
Questa operazione di eliminazione sulla collezione users rimuove tutti i documenti che
corrispondono ai criteri di status pari a D.
Per impostazione predefinita, il metodo db.collection.remove() rimuove tutti i documenti
che corrispondono alla sua ricerca; tuttavia, il metodo può accettare un flag per limitare
l'operazione di cancellazione di un singolo documento.
2.2 Aggregazione
Le aggregazioni sono operazioni che registrano i dati dei processi e restituiscono risultati
calcolati.
MongoDB fornisce un ricco insieme di operazioni di aggregazione che esaminano ed
eseguono calcoli su set di dati.
L'esecuzione dell'aggregazione dei dati nell'istanza mongod semplifica il codice
dell'applicazione e i requisiti per le risorse limiti.
Come query, tali operazioni utilizzano collezioni di documenti come input e restituiscono i
risultati sotto forma di uno o più documenti.
MongoDB 2.2 ha introdotto un nuovo metodo, detto, Aggregration Framework, modellato
sul concetto di pipeline per l'elaborazione dei dati. [7]
I documenti entrano in una pipeline a più livelli che trasforma i documenti in un risultato
aggregato.
Le fasi della pipeline più elementari prevedono filtri che funzionano come le query e
trasformazioni di documenti che modificano la forma del documento di output.
Altre operazioni di pipeline forniscono strumenti per il raggruppamento e l'ordinamento dei
documenti da un dato campo o campi specifici, nonché strumenti per aggregare i contenuti
20
di array, compresi gli array di documenti.
Inoltre, le fasi della pipeline possono utilizzare gli operatori per attività quali il calcolo della
media o concatenare una stringa.
La pipeline prevede un efficiente aggregazione dei dati utilizzando le operazioni all'interno
di MongoDB, ed è il metodo preferito per l'aggregazione.
MongoDB fornisce il metodo db.collection.aggregate() nella shell Mongo e il comando
aggregate per l'aggregazione pipeline.
Esempio:
La pipeline di aggregazione ha due fasi: $match e poi $group .
MongoDB fornisce anche il metodo Map-Riduce per eseguire l'aggregazione.
In generale, le operazioni di map-riduce hanno due fasi: una di map (mappa) che elabora
ciascun documento ed emette uno o più oggetti in ciascun documento di input; mentre
riduce (ridurre) combina l'output dell'operazione map.
Inoltre, map-riduce può avere una fase di finalizzazione che apporta modifiche finali al
risultato.
Come altre operazioni di aggregazione, map-reduce può specificare una condizione di query
21
per selezionare i documenti di ingresso, nonché di ordinamento e limitare i risultati.
Map-riduce utilizza funzioni JavaScript personalizzate per eseguire la mappa e ridurre le
operazioni, così come l'operazione di finalizzazione opzionale.
Mentre JavaScript fornisce una grande flessibilità rispetto alla pipeline di aggregazione, in
generale, map-reduce è meno efficiente e più complesso di quanto la pipeline di
aggregazione.
Esempio:
Schema del funzionamento map-reduce annotato.
Le operazioni di aggregazione con il comando aggregate hanno le seguenti limitazioni:
•
Se il comando restituisce un aggregato che contiene un set di risultati completo;
allora il comando produrrà un errore se il set di risultati supera il limite di
dimensione BSON, che è di 16 MB.
Per gestire un set di risultati che supera questo limite, il comando aggregate deve
restituire un cursore o memorizzare i risultati in una collezione.
•
Gli stadi della pipeline hanno un limite di 100 MB di RAM; se una fase supera
questo limite, si produce un errore; per consentire la gestione di grandi insiemi di
dati, si utilizza l'opzione allowDiskUse che abilita la scrittura su file temporanei.
22
2.3 Indicizzazione
Un indice è una struttura dati che permette la localizzazione rapida delle informazioni
relative al campo su cui è costruito e riveste un ruolo fondamentale in fase di
ottimizzazione delle query. [7]
In MongoDB tutti gli indici sono basati su B-tree.
Quando un indice “copre” la query,ovvero quando tutti i campi della query fanno parte
dell’indice sia come condizione sia come valore ritornato, MongoDB può eseguire
l’interrogazione senza accedere alla collection, massimizzando così le prestazioni.
É possibile creare un indice su qualsiasi campo di un documento, indipendentemente dal
tipo di dato, rendendo così possibile l’indicizzazione di campi complessi quali array e
documenti.
Sono supportati indici composti con la possibilità di specificare l’ordinamento ascendente
o discendente per ogni campo.
Un esempio di creazione di indice composto è mostrato in Fig. 6
Figura 6:
esempio creazione indice
L’esecuzione di questo comando crea un indice sui campi nome, cognome e data di
nascita.
Per i primi due elementi è specificato un ordinamento ascendente, mentre la data di nascita
è ordinata iniziando dalla più recente.
Le query possano utilizzare anche parti di un indice composto, purché i campi richiesti
siano un prefisso di quelli indicizzati; nell’esempio fornito l’indice può assolvere query
che esprimono condizioni su nome e cognome, ma non interrogazioni su nome e data di
nascita.
23
Di solito quando viene imposto un indice su un campo, qualora quest’ultimo non
fosse presente in un documento lo si considera indicizzato con il valore NULL.
Questo comportamento potrebbe però essere indesiderato in caso si abbiano numerosi
documenti il cui valore indicizzato sia assente, pertanto è possibile specificare l’attributo
“sparse” dell’indice in modo da non considerare i valori non presenti.
Un altro attributo molto importante è il Time To Live (TTL).
Questo attributo, applicabile solo con campi di tipo datetime, permette a MongoDB di
rimuovere automaticamente il documento dopo un certo intervallo di tempo definito a
priori.
Un indice TTL assume particolare importanza in scenari di logging o memorizzazione di
sessioni utente, dove documenti datati non ricoprono più ruoli significativi.
Non tutti i tipi di indici sono mirati all’ottimizzazione delle query, infatti alcuni di essi
aumentano le possibilità di interrogazione offrendo nuove funzionalità nella ricerca dei
dati.
Gli indici che fanno parte di questa categoria sono:
•
Indici hashed: introdotti nella versione 2.4 di MongoDB, permettono di
indicizzare uno o più campi attraverso il corrispettivo valore hash.
Questa possibilità acquisisce particolare importanza in ambiente distribuito, qualora
si volesse imporre come chiave di shard, ovvero il campo su cui viene basata la
suddivisione dei dati tra le diverse macchine, un attributo monotonicamente
crescente quale una data.
In questa situazione, usando un campo su cui è costruito un indice hashed, si evita
l’addensamento dei risultati più recenti e si permette una distribuzione dei dati
maggiormente omogenea.
•
Indici geospaziali: basati anch’essi su B-tree, permettono interrogazioni
multidimensionali tramite coordinate, sia su superfici piatte che sferiche.
Le operazioni permesse sono query di inclusione (determinazione degli elementi
all’interno di un dato poligono), intersezione (applicabili solo su superfici
sferiche, permettono di individuare gli elementi che intersecano un particolare
24
oggetto) e prossimità (individuazione degli elementi più vicini a una data
posizione).
•
Indici text: introdotti in versione sperimentale con la release 2.4, permettono la
ricerca full-text sui campi indicizzati.
Questo particolare tipo di interrogazione prevede la ricerca di una o più keyword su
dati di dimensione notevole.
Sono inoltre disponibili funzionalità di stemming (determinazione della radice di una
parola) che permettono una ricerca più completa, seppur con la possibilità di
generare falsi positivi.
Effettuata una query, a ogni elemento risultante dall’interrogazione viene attribuito
un punteggio, in modo da ottenere un vettore risultati ordinato per significatività
rispetto ai termini cercati.
2.4 Replicazione
La replicazione crea copie multiple dei dati su nodi diversi, così da avere server di backup
o disaster recovery sempre aggiornati.
In tal modo è possibile prevenire l’integrità dei dati in situazioni dannose come
l’interruzione dell’alimentazione. [7]
In MongoDB la replicazione permette di aumentare l’accessibilità dei dati e le
performance di lettura, infatti per un client è possibile impostare il driver in modo che sia
consentita la lettura dei dati da qualsiasi membro di un replica set.
MongoDB implementa i meccanismi di replicazione dei dati basandosi su un modello
master/slave con failover automatico, chiamato replica set (Fig.7).
25
Figura 7:
esempio di replica set con 3 nodi
Un replica set prevede un nodo primario a cui vengono indirizzate tutte le operazioni
sui dati, sia di lettura che di scrittura.
Effettuata l’interrogazione, il server primario registra l’operazione sul proprio “oplog”,
una capped collection salvata nel database locale che si occupa di memorizzate un set di
informazioni minime necessarie per la riproduzione dell’operazione.
Esempio:
Il campo “ts” rappresenta il timestamp, dove il primo valore è dato dai secondi in formato
unix-time e il secondo è un contatore, che permette di identificare correttamente il record.
Il successivo campo “op” mostra l’operazione eseguita, codificata in forma abbreviata; in
questo caso il valore “i” indica un inserimento.
Il terzo campo mostra il namespace di riferimento, ovvero l’accoppiata database e
collection.
Infine, l’ultimo campo è l’elemento che è stato soggetto a manipolazione; nel caso di
inserimento si ha il documento aggiunto.
26
Periodicamente i server secondari analizzano il proprio oplog ottenendo il timestamp
dell’ultima operazione; dopodiché si occuperanno di interrogare l’oplog del server
primario alla ricerca di tutte le operazioni temporalmente successive al timestamp cercato
precedentemente.
Le operazioni individuate verranno copiate nell’oplog del nodo secondario, per poi essere
applicate sulla propria copia dei dati concludendo il ciclo di replicazione.
Il procedimento è asincrono, ma è possibile che i server secondari presentino copie dei dati
più vecchie dell’attuale.
Al contrario, la scrittura delle informazioni nel journal (qualora sia attivato) e nell’oplog
avviene attraverso una transazione atomica.
Una delle caratteristiche più diffuse nei database NoSQL è la capacità di autogestione del
sistema in caso di malfunzionamento.
MongoDB implementa una logica di automate failover basata su votazione dei server
secondari.
All’interno di un replica set, tutti i nodi sono in contatto tra loro attraverso lo scambio di
piccoli messaggi di stato, denominati heartbeat.
Quando il server primario non è più raggiungibile, i nodi secondari riconoscono
l’interruzione dei messaggi e si preparano a eleggere un successore.
Ogni server ha una priorità, impostato a priori in fase di configurazione.
Quando i server secondari dovranno stabilire quale sarà il nuovo nodo primario, i membri
con maggiore priorità confronteranno tra di loro il periodo di tempo trascorso dall’ultima
sincronizzazione dei dati (Fig. 8).
Il nodo secondario con la maggior priorità e i dati più recenti verrà eletto a server
primario.
27
Figura 8:
elezione nuovo membro primario in un replica set
Individuato un server primario è possibile che si verifichino scenari di spareggio, dove più
server siano eleggibili.
Per evitare tali situazioni, è possibile dichiarare una macchina “arbitro” il cui scopo sia
quello di stabilire il successore.
Un server arbitro non richiede necessariamente hardware dedicato in quanto non contiene
frammenti o copie dei dati, dunque il consumo di risorse è minimo e può risiedere su
macchine già in uso (Fig. 9).
Quando è in corso una votazione viene imposto un lock sul database e non sono permesse
scritture per tutta la durata dell’elezione, operazione che impiega un tempo inferiore al
minuto.
Figura 9:
schema con nodo primario, secondario e arbitro
28
2.5 Sharding
MongoDB per la distribuzione dei dati e per le operazioni su un cluster di macchine usa
politiche di sharding, ovvero un meccanismo di scaling orizzontale che divide il dataset
su server differenti, denominati shard. [7]
Ogni shard contiene una partizione dei dati in un database indipendente capace di esistere
in modo autonomo.
Al crescere delle dimensioni del database, il meccanismo di scaling orizzontale trova
sempre maggiore utilizzo, soprattutto quando è possibile dividere i dati in porzioni
sufficientemente piccole per risiedere interamente in memoria centrale, annullando lo
swapping su disco e ottenendo quindi un notevole incremento prestazionale.
Lo sharding è molto importante anche per il recupero dei dati; infatti ogni shard si troverà
a lavorare su un numero minore di documenti e potrà concludere le interrogazioni nel
minor tempo possibile.
Un cluster basato su sharding prevede i seguenti componenti (Fig.10):
•
Cluster server: chiamati anche con il termine “query router”, si occupano della
ricezione delle query e del relativo instradamento agli shard. Sono processi non
persistenti, le cui informazioni vengono memorizzate in memoria centrale e non
richiedono supporto su unità fisiche.
Un sistema distribuito può avere diversi cluster server, sia per distribuire il carico di
richieste su più macchine, sia per motivi di tolleranza ai guasti.
•
Configuration server: garantisce la memorizzazione e la persistenza dei metadata
del cluster.
Tra le informazioni più importanti salvate in tal server troviamo il percorso di rete
degli shard facenti parte dell’infrastruttura, la suddivisione dei valori con cui avviene
la distribuzione e uno storico delle migrazioni dei dati.
La loro presenza è resa necessaria a causa della volatilità delle informazioni gestite
dai cluster server, i quali dispongono così di un nodo centrale a cui fare riferimento.
29
•
Shard: server stand-alone in cui risiedono le porzioni del dataset distribuito.
A uno shard può anche corrispondere un replica set, in modo da incrementare la
tolleranza ai guasti; in questo caso, il cluster server instraderà le interrogazioni al
server primario del replica set, il quale eseguirà la query e permetterà la replicazione
delle operazioni nei server secondari.
Figura 10:
schema cluster basato su sharding
La distribuzione dei dati viene effettuata tramite una shard key, ovvero un campo di un
documento su cui effettuare la partizione per range di valori.
La scelta della chiave di shard riveste un ruolo di fondamentale importanza e determina sia
l’equità della distribuzione, sia le prestazioni del sistema.
Una suddivisione ben bilanciata permette di ottenere un carico di lavoro medio uguale per
tutte le macchine del sistema, diminuendo così la probabilità di avere colli di bottiglia
e code di attesa.
Non è sempre possibile però ottenere una distribuzione perfettamente bilanciata, come per
esempio nel caso si avesse la necessità di usare come chiave di shard un campo
monotonicamente crescente come una data.
In questo caso sussiste un ulteriore problema, qualora le operazioni più frequenti e
computazionalmente pesanti avvenissero solo sui dati più recenti; allora la maggioranza
delle macchine risulterebbe inutilizzata, mentre le rimanenti sarebbero sottoposte a un
pesante carico di lavoro.
30
Per cercare di evitare queste situazioni è possibile utilizzare come chiave di shard uno o più
campi su cui sia stato costruito un indice hashed, in modo da ottenere una distribuzione
equa su tutti gli shard.
L’elemento che ha il compito di mantenere il più possibile equilibrata la distribuzione è il
balancer.
Tale processo agisce sui chunk, ovvero piccole porzioni di dati di dimensione prefissata
(di solito 64 MB).
Periodicamente il balancer controlla che la differenza in numero di chunk tra lo shard più
popolato e quello meno popolato sia inferiore a un certo limite (Fig.11).
Figura 11:
Tabella chunk
Quando viene rilevato un numero di chunk superiore al limite concesso, viene avviato il
processo di migrazione dei dati dallo shard sovrappopolato a quello numericamente in
difetto.
In caso di esigenze particolari è possibile disattivare il balancer e provvedere alla
migrazione manuale dei singoli chunk, anche in modo non bilanciato; questa operazione
può risultare vantaggiosa nel caso il cluster sia composto da macchine dalle prestazioni
molto diverse tra loro e che richiedono dunque carichi di diversa entità.
Un ultimo aspetto da analizzare in un ambiente basato su cluster, è dato dalle modalità con
cui il cluster server può instradare le richieste ai vari shard.
Due scenari:
• Instradamento broadcast
• Instradamento verso singolo shard o piccoli gruppi
31
Una query è inviata in modalità broadcast ogni volta che il cluster server non sia in grado
di determinare quali shard contengano il dato richiesto.
Alla ricezione di una query, il cluster server determina come prima cosa se è possibile
limitare l’instradamento a un certo numero di shard, per poi inviare loro la query e
stabilire per ognuno di essi un cursore.
Quest’ultimo sarà usato successivamente per ricomporre i risultati ottenuti, con modalità
diverse in base al tipo di dato risultante.
In caso nell’interrogazione sia presente un ordinamento verrà eseguito un merge sort sui
risultati prima di presentare il dato al client, mentre nel caso i dati non siano richiesti
ordinati, viene ritornato un cursore che si occuperà di accedere ai frammenti di risultato
secondo politiche di round robin.
32
Capitolo 3: Gestione e Amministrazione di MongoDB
MongoDB Inc. permette di usufruire del servizio di monitoraggio e di backup (MMS).
Infatti tramite l'installazione di un agent locale, che invia dati al server MMS nel cloud,
permette di monitorare le istanze di mongodb senza dover installare software. [1]
Oltre a MMS ci sono alcuni tools già presenti nell'installazione di MongoDB:
•
Mongo Shell: permette agli sviluppatori di vedere, inserire, rimuovere e aggiornare
i dati nei loro database, come di ottenere informazioni sulla replicazione, sulla
configurazione dello sharding, spegnere i server, eseguire del codice JavaScript e
molto altro.
•
Mongostat: è un tool command-line che visualizza una lista riassuntiva delle
statistiche di un'istanza di MongoDB in esecuzione: quante insert, update, query
sono eseguite così come che percentuale di tempo il database è rimasto lockato e
quanta memoria sta usando.
•
Mongotop: è un tool command-line che fornisce un metodo per tracciare la
quantità di tempo usata da un'istanza di MongoDB a leggere e a scrivere dati.
Esso fornisce statistiche a livello di collection; di default ritorna valori pari al
secondo.
•
Mongosniff: è un tool command-line che fornisce uno sniffing a basso livello
dell'attività del database monitorando il traffico di rete in entrata ed in uscita.
Esso richiede la libreria di rete Libpcap ed è disponibile solo per i sistemi UNIXlike.
•
Mongoimport-Mongoexport: Mongoimport è un'utility command-line per
importare il contenuto da un file JSON, CSV o TSV creato da mongoexport o da
qualunque export di terze parti che ne rispettano il formato.
•
Mongodump-Mongorestore: Mongodump è un'utility command-line per creare un
export binario del contenuto di un database MongoDB; Mongorestore può essere
usato per ricaricare un dump fatto con mongodump.
33
3.1 Shell Mongo
Il server di MongoDB viene attivato tramite un eseguibile chiamato mongod.
La shell di MongoDB viene attivata caricando l’eseguibile mongo che si connette a uno
specifico processo mongod.
Tale shell è basata sul linguaggio JavaScript ed è utile perché consente di amministrare e
modificare i dati del database.
Una volta attivata la shell, se nessun database viene specificato all’avvio, la shell seleziona
un database di default chiamato test. [7]
Infatti creare un database o una collezione non è necessario in MongoDB, poiché un
database o una collazione vengono generati solo quando viene inserito un documento.
Poiché si sta usando la shell in JavaScript i documenti devono essere salvati nel formato
JSON.
I comandi di amministrazione di MongoDB forniscono una serie di strumenti per ottenere
delle informazioni sul processo mongod.
Essi sono:
•
show dbs: Stampa una lista di tutti i database presenti nel sistema.
•
show collections: Dopo aver selezionato un database, il comando stampa una lista
di tutte le collezioni presenti nel database.
•
db.stats(): Fornisce una serie di informazioni sul database che si sta usando come
ad esempio, il numero di collezioni, numero di oggetti, la dimensioni dei dati, la
dimensione di memorizzazione dei dati, il numero di indici e la loro dimensione.
Il metodo stats() può essere applicato anche ad una collezione.
•
use: tale comando permette di selezionare il database su cui lavorare.
•
db.help(): utile per vedere quali sono i comandi che MongoDB può utilizzare per
interrogare i database e il sistema.
3.2 Gestione della Memoria
La memoria in MongoDB è sviluppata per avere una maggior semplificazione e sfrutta il
34
concetto di file mappato in memoria in modo persistente, dove ai segmenti della memoria
virtuale viene assegnata una correlazione byte a byte con una porzione del file fisico in cui
risiedono i dati (Fig.12).
Figura 12:
schema mappatura memoria
La memoria virtuale è una rappresentazione a livello di processo della memoria fisica e
permette di astrarre l’indirizzamento su di essa.
Se vediamo la memoria dal punto di vista del processo, un file mappato in memoria
virtuale ha le stesse metodologie di accesso di un array di byte.
L’indirizzamento della memoria virtuale su quella fisica è compiuta in modo trasparente
dal S.O., che si occupa di gestire la complessità di tutte le operazioni di accesso.
Quando il processo richiede una pagina non ancora mappata in memoria, si genera un
page fault, a cui il sistema operativo farà seguire la ricerca dell’informazione su disco.
Quando si conclude questa operazione si possono presentare due casi: nel primo se la
memoria fisica non è interamente occupata, la nuova pagina viene semplicemente caricata
in RAM, mentre nel secondo se non esiste spazio libero a disposizione, il S.O. si occuperà
di scambiare la nuova pagina con una già presente in memoria (swap-out).
Sia l’accesso su disco che lo scambio di pagine sono operazioni costose in termini di
tempo d’esecuzione e, per minimizzarne tale parametro, MongoDB tende a caricare e
mantenere in memoria l’intero database o una sua porzione significativa.
35
Infatti all’avvio dell’istanza di MongoDB, i data file vengono mappati in memoria
attraverso la system call mmap().
Una volta caricato il contenuto del file nella sezione di memoria virtuale denominata
shared view, è possibile modificare i dati lasciando al S.O. la complessità di salvataggio su
disco delle modifiche effettuate al database,a sua volta suddiviso in extent (porzioni fisiche
di dati di dimensione preallocata), operazione che viene eseguita in piena autonomia da
quest’ultimo a intervalli regolari.
Per aver maggior periodicità, MongoDB impone comunque al S.O. di aggiornare il data
file ogni 60 secondi.
Insieme a tal metodo, va considerato il formato nativo dei salvataggi, cioè BSON, che può
salvare documenti grandi al massimo 16 MB.
Se si devono utilizzare documenti più grandi si utilizza il sistema apposito GridFS, che
consente di suddividere un singolo documento in chunks. [7]
Invece di memorizzare un file in un unico documento, GridFS divide un file in chunks, e
ciascuno di questi chunks vengono messi in documenti separati.
Di default, GridFS limita la dimensione del blocco a 255k.
GridFS utilizza due collection per memorizzare i file; una collection memorizza i chunks e
un'altra i file di metadati.
Quando si esegue una query si riassemblano i chunks in base alle esigenze.
36
Conclusioni
Abbiamo visto e analizzato come MongoDB sia diventato il database per le applicazioni di
oggi: innovativo, veloce, scalabile, affidabile e poco costoso per funzionare.
Infatti secondo "db-engines.com", ad Aprile 2014 MongoDB e' al 5º posto della classifica
dei database più popolari del mondo e al 1º posto dei database NoSQL. [1]
Attualmente alcuni utilizzatori più significativi di MongoDB sono:
•
SAP: usa MongoDB in SAP platform-as-a-service.
•
Forbes: memorizza articoli e dati societari in MongoDB.
•
The New York Times: usa MongoDB nella sua applicazione di caricamento di
fotografie.
•
The Guardian: usa MongoDB per il suo sistema di identificazione.
•
Foursquare: implementa MongoDB su Amazon AWS per memorizzare località e
le registrazioni degli utenti nelle località.
•
Ebay: usa MongoDB per i suggerimenti della ricerca e per State Hub, il Cloud
Manager interno.
37
Bibliografia
[1]
Wikipedia: MongoDB, http://it.wikipedia.org/wiki/mongoDB, 14/09/2014.
[2]
Wikipedia: NoSql, http://it.wikipedia.org/wiki/NoSQL, 14/09/2014.
[3]
Blog.Artera.it: Sql e NoSql: cosa sapere sui database non relazionali,
http://blog.artera.it/programmazione/sql-nosql-database-non-relazionali, 14/09/2014.
[4]
Couchbase_Inc._In,
http://www.couchbase.com/press-releases/couchbase-survey-
shows-accelerated-adoption-nosql-2012, 7/10/2014.
[5]
Wikipedia: Teorema CAP, http://it.wikipedia.org/wiki/Teorema_CAP, 7/10/2014
[6]Mongo_Architecture_Guide,http://info.mongodb.com/rs/mongodb/images/MongoDB_
Architecture_Guide.pdf, 14/09/2014.
[7]
MongoDB_Documentation, http://docs.mongodb.org/manual, 14/09/2014
38