NOSQL
MongoDB
Origini
MongoDB deriva il suo nome dalla parola "huMONGOus”, che significa enorme. E'
attualmente il il database NoSQL più diffuso*.
Sviluppato inizialmente dalla società di software 10gen (ora MongoDB Inc.) nel 2007
come un componente di un altro prodotto. Nel 2009 viene rilasciato in open-source
come database a se stante.
*Fonte del dato della diffusione dei DB: http://db-engines.com/en/ranking
MongoDB
Datamodel
MongoDB è un document store: ogni record viene memorizzato come un documento;
esso può avere un numero arbitrario di campi aventi una qualsiasi lunghezza.
Rispetto al modello relazionale: si accorpano quanto più possibile gli oggetti, creando
delle macro entità dal massimo contenuto informativo.
MongoDB non possiede uno schema e ogni documento non è strutturato, ha solo una
chiave obbligatoria: _id , la quale è utilizzata per identificare univocamente il
documento.
MongoDB
BSON e JSON
Il formato utilizzato per il salvataggio e la trasmissione dei document in MongoDB è il
BSON (JSON Binario).
Il formato JSON (JavaScript Object Notation) è largamente diffuso nel mondo web,
esso è
un formato di testo completamente indipendente dal linguaggio di
programmazione utilizzato ed è basato su due strutture:
- un insieme di coppie nome/valore;
- un elenco ordinato di valori.
MongoDB
Esempio di JSON
{
"_id" : 1,
"name" : { "first" : "John", "last" : "Backus" },
"contribs" : [ "Fortran", "ALGOL", "Backus-Naur Form", "FP" ],
"awards" : [
{
"award" : "W.W. McDowell Award",
"year" : 1967
},
{
"award" : "Draper Prize",
"year" : 1993
}
]
}
MongoDB
BSON e JSON
II formato BSON estende il modello JSON allo scopo di aggiungere ulteriori tipi di dati
(ad esempio il formato data e il byte array) e garantire una maggiore efficienza
nell'interazione tra diversi linguaggi di programmazione.
La dimensione massima di un singolo documento BSON è di 16MB.
Ad ogni document è associato un _id che agisce come object identifier ed è composto
da 12 byte come segue:
MongoDB
BSON e GridFS
La dimensione del BSON potrebbe essere limitante.
Si può utilizzare la modalità in GridFS che, invece che effettuare un'unica
memorizzazione, divide i file in più parti (chunk) di dimensione prestabilita e li
memorizza come document separati.
Il limite di default di un chunk è 255kB.
Vengono utilizzate due collection per memorizzare i file:
- una per i chunk;
- una per i metadati.
Sarà compito del driver o del client riassemblare i chunk necessari.
MongoDB
Datamodel
In MongoDB il document è l'unità di base del database ed equivale ad una riga dei
database relazionali.
La tabella seguente riassume le “equivalenze” di termini fra MongoDB e gli RDBMS:
MongoDB
RDBMS
Database
Database
Collection
Tabella
Document
Record
Field
Colonna
MongoDB
Datamodel
Database
Collection
Collection
Collection
Document
Document
_id
Field1
Field2
Document
Document
_id
Field1
Field3
Field4
Architettura
MongoDB
Architettura Base
MongoDB è organizzato secondo una architettura base Client/Server.
Il client può essere:
- shell mongo (implementata in JavaScript)
- un applicativo scritto in diversi linguaggi di programmazione
mongod
client
Oltre a questa configurazione base è possibile incrementare la ridondanza e la
disponibilità del dato attraverso la configurazione di un “replica set”
MongoDB
Architettura con Replica Set
Un replica set è un gruppo di istanze mongod che
gestiscono lo stesso insieme di dati.
Tutte
Primary
le
modifiche
che
vengono
effettuate
unicamente sul primary, vengono scritte in un log
che viene poi distribuito in maniera asincrona ai
Secondary
Secondary
Replica set
client
secondary i quali poi replicano le modifiche sui loro
dati.
I client leggono i dati dal primary, ma possono
essere configurati anche per leggere i dati dai
secondary.
Il dato restituito da un secondary potrebbe non
essere aggiornato.
MongoDB
Architettura con Replica Set
Nel caso in cui il server primario non
Lettura
fosse più disponibile un meccanismo
Scrittura
eleggere un nuovo server primario fra
Driver
di automatic election provvede ad
Mongod: Primary
i secondari.
È
possibile
prevedere
Lettura
Mongod: Secondary
Lettura
Mongod: Secondary
un'ulteriore
istanza di mongod, avente il ruolo di
votazione durante l'elezione del nuovo
Mongod: Primary
Driver
arbiter, che viene utilizzato solo per la
primary.
Repliche
asincrone
Lettura
Mongod: Primary
Scrittura
Lettura
Mongod: Secondary
Replica
asincrona
MongoDB
Introduzione Architettura Sharding
L'architettura appena vista presenta potenziali problemi:
- all'aumentare del numero di query è possibile che si possa esaurire la capacità di CPU
del server;
- all'aumentare delle dimensioni dei dati c'è il rischio di superare la capacità del disco di
una singola macchina;
- dati di lavoro di dimensioni superiori alla RAM del sistema potrebbero “stressare” la
velocità di I / O dei dischi .
Possibili soluzioni:
- scalare verticalmente (aumento CPU, HD e ram);
- scalare orizzontalmente (sharding).
MongoDB
Architettura Sharding
MongoDB può scalare orizzontalmente in maniera lineare.
L'architettura Sharding è costituita da tre componenti:
- Shards: immagazzinano i dati, per garantire la consistenza e la disponibilità dei dati
sono spesso costituiti da dei Replica Sets;
- Query Routers o istanze mongos: interfacce che ridirezionano le richieste verso i
shards corretti e restituiscono il risultato ai vari client.
- Config servers: contengono i metadati che descrivono il mapping tra dati e shards.
MongoDB
Architettura Sharding
Mongod: Primary
Mongod: Primary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Shard 1
Shard N
Config
Config
mongos
mongos
Config
client
MongoDB
Architettura Sharding
Il partizionamento ed il bilanciamento è automatico in base alla Shard Key (un field
del document) che si decide di utilizzare.
È poi possibile decidere, per la suddivisione dei dati in chunks, se utilizzare:
Range Based Sharding
Hash Based Sharding
mongod
Key-range (0...99)
mongod
Key-range (0...33)
mongod
Chunk 1
Hash Function
Key-range (67...99)
mongod
Chunk N
mongod
Chunk 1
mongod
Chunk N
Query Language
MongoDB
Datatype
Datatype
Descrizione
Boolean
Tipo booleano true/false
Arrays
Utilizzato per memorizzare array o liste di valori
Integer
Valori numerici (32-bit o 64-bit a seconda del server.
Double
Valori numerici in virgola mobile
String
Stringa in formato UTF-8
Symbol
Come una stringa, ma utilizzato nei linguaggi che usano il tipo
symbol (es. Ruby)
Regular expression
Utilizzato per memorizzare espressioni regolari
Date
Memorizza il tempo e la data in formato Unix.
Timestamp
Memorizza un Timestamp
Binary data
Memorizza dati in formato binario
Code
Utilizzato per memorizzare codice Javascript nel document
Null
Memorizza un valore null
Object ID
Memorizza l'ID del document
Object
Utilizzato per gli embedded document
Min/Max key
Questi vengono usati solo internamente e servono per comparare al
valore minimo e massimo di un dato BSON
MongoDB
Query Language – Uso dei database
Per selezionare un database:
> use DATABASE_NAME
Questo comando seleziona il database e, qualora non esista, lo crea.
Per vedere quali database sono presenti all'interno di un server possiamo usare il
comando
> show dbs
local
0.78125GB
test
0.23012GB
vengono mostrati tutti i database sui quali è stato fatto almeno un insert e la
dimensione che occupano.
MongoDB
Query Language – Uso dei database
Per sapere quale database è stato selezionato si usa il comando:
> db
viene restituito il nome del database in uso.
Per eliminare un database occorre selezionarlo attraverso use e poi dare il comando:
> db.dropDatabase()
se per caso non viene selezionato nessun database e viene dato il comando
db.dropDatabase viene eliminato il database di default.
MongoDB
Query Language – Collection
Una volta che abbiamo creato il database possiamo creare le collection al suo interno.
Possono essere usati due metodi:
- il primo prevede una creazione esplicita
> db.createCollection(name, options)
- il secondo prevede una creazione implicita
> db.users.insert(document)
Per visualizzare tutte le collection presenti in un database si usa il comando:
> show collections
MongoDB
Query Language - Insert
Con il comando insert possiamo inserire un document all'interno di una collection.
> db.docs.insert({
_id: ObjectId(7df78ad8902c),
title: 'Dispense MongoDB',
description: 'Dispense corso DASSIA su MongoDB',
by: 'CRS4',
url: 'http://dassia.crs4.it/',
tags: ['mongodb', 'database', 'NoSQL']
})
Se l'_id non viene specificato questo viene assegnato in maniera automatica.
MongoDB
Query Language - Find
Il metodo “base” per effettuare le query è find: restituisce un cursore ai dati
corrispondenti e supporta sia criteri per la query che le proiezioni dei dati.
Possono poi essere applicati dei modificatori al cursore.
db.users.find(
MongoDB
{ age: {$gt: 18 } },
← criteri di query
{ name : 1, address: 1}
← proiezione
) . limit ( 5 )
SQL
← collection
← modificatore di cursore
SELECT _id, name, address
← proiezione
FROM users
← tabella
WHERE age > 18
← criteri di selezione
LIMIT 5
← modificatore di cursore
MongoDB
Query Language - Find
L'uso del metodo find è il seguente
> db.COLLECTION_NAME.find({QueryCriteria},{Projection})
dove i QueryCriteria e la Projection sono opzionali.
Vanno racchiusi fra parentesi grafe e possono essere più di uno: eventuali criteri di
selezione vanno indicati singolarmente.
Se si vuole effettuare una proiezione senza criteri di selezione:
> db.COLLECTION_NAME.find({},{Projection})
Se non si ha nessuna proiezione da fare, ma solo delle selezioni la sintassi è
> db.COLLECTION_NAME.find({QueryCriteria})
MongoDB
Find – Query Criteria
Criterio
Sintassi
Esempio
Uguaglianza
{<key>:<value>}
db.users.find({“name” : “Nicola” })
Minore di
{<key>:{$lt:<value>}}
db.users.find({ age : {$lt:25}})
Minore o uguale di
{<key>:{$lte:<value>}}
db.users.find({ age : {$lte:25}})
Maggiore di
{<key>:{$gt:<value>}}
db.users.find({ age : {$gt:18}})
Maggiore o uguale di
{<key>:{$gte:<value>}}
db.users.find({ age : {$gte:19}})
Non uguale
{<key>:{$ne:<value>}}
db.users.find({ age : {$ne:45}})
MongoDB
Find – Query Criteria
Il metodo find supporta criteri multipli in AND, OR o combinazione di questi:
AND (tutti gli utenti di 18 anni che si chiamano Nicola)
> db.users.find({ age: 18, name: 'Nicola'})
OR (tutti gli utenti di 18 anni e tutti gli utenti che si chiamano Nicola)
> db.users.find({ $or: [{age: 18}, {name: 'Nicola'}] })
AND e OR (tutti gli utenti di 18 anni e che contemporaneamente si chiamano Nicola o vivono
a Cagliari)
> db.users.find({ age: 18,
$or:[ {name: 'Nicola'}, {town: 'Cagliari'} ]
})
MongoDB
Find – Projection
I criteri di proiezione servono ad indicare quali attributi del documento restituire.
db.users.find( { age: 18 } , { name: 1} )
{ _id: 1, age: 18, name: 'Almo' }
{ _id: 2, age: 28, name: 'Asia' }
projection
query criteria
{ _id: 3, age: 21, name: 'Giada' }
{ _id: 1, age: 18, name: 'Almo' }
{ _id: 1, name: 'Almo' }
{ _id: 4, age: 38, name: 'Ugo' }
{ _id: 5, age: 18, name: 'Sofia' }
{ _id: 5, name: 'Sofia' }
{ _id: 5, age: 18, name: 'Sofia' }
{ _id: 6, age: 58, name: 'Guido' }
Bisogna notare che _id viene sempre restituito, se non lo si vuole visualizzare va
messo a zero.
db.users.find( { age: 18 } , { name: 1, _id : 0} )
MongoDB
Cursor Modifier: Limit e Skip
Il cursore ai dati restituito da un find può essere modificato mediante l'utilizzo di un cursor
modifier. Abbiamo quindi:
limit che viene utilizzato per restituire i primi N documenti trovati.
> db.COLLECTION_NAME.find().limit(N)
skip che permette di “saltare” i primi M documenti trovati.
> db.COLLECTION_NAME.find().skip(M)
Ad esempio
> db.users.find({},{name:1,_id:0}).limit(1).skip(2)
restituisce il nome del terzo documento.
MongoDB
Sort e Distinct
sort che esegue l'ordinamento per la chiave specificata ascendente (1) o discendente (-1)
> db.COLLECTION_NAME.find().sort({key:1})
distinct se volessi trovare tutti i valori diversi presenti per uno o più field in una collection
> db.COLLECTION_NAME.distinct('key')
Ad esempio, la seguente trova tutti i nomi presenti nella collection
> db.users.distinct('name')
mentre la seguente restituisce tutti i nomi distinti dei maggiorenni presenti nella collection
> db.users.distinct('name',{age:{$gte:18}})
MongoDB
Count
Per contare il numero di documenti può essere usato il metodo count() sia in questo
modo:
> db.COLLECTION_NAME.count()
che
> db.COLLECTION_NAME.find().count()
Se vogliamo specificare dei criteri di query, ricorrendo alla programmazione funzionale,
abbiamo:
db.users.count( { age: { $gt: 30 } })
oppure
db.users.find( { age: { $gt: 30 } }).count()
MongoDB
Update e Save
Per aggiornare un document si hanno due metodi a disposizione:
- update: aggiorna i field che vengono indicati nel document;
- save: rimpiazza il document esistente con uno nuovo.
Ad esempio abbiamo:
> db.users.update({name: 'Pino'}, {$set:{name: 'Ugo'}})
> db.users.save({_id:ObjectId(),NEW_DATA})
MongoDB
Remove Document
Per rimuovere un document si utilizza remove.
> db.COLLECTION_NAME.remove(DELETION_CRITERIA)
Ad esempio
> db.users.remove({name: 'Maurizio'})
Mentre la sintassi per rimuovere il primo document dove compare il criterio è:
> db.COLLECTION_NAME.remove(DELETION_CRITERIA, 1)
Per rimuovere tutti i document da una collection devo usare
> db.COLLECTION_NAME.remove()
MongoDB
Indici Secondari
Tutti i documenti sono indicizzati per la loro chiave primaria _id.
Ciò significa che utilizzando il metodo find con alcuni criteri di ricerca controlleremo tutti
i documenti nella loro interezza. Per snellire queste operazioni si possono creare degli
indici secondari tramite il comando ensureIndex.
> db.COLLECTION_NAME.ensureIndex(keys,options)
Posso ad esempio specificare uno o più field sui quali generare gli indici sia in maniera
ascendente (1) che discendente (-1).
> db.users.ensureIndex({'name' : 1, 'age' : -1})
MongoDB
Aggregazione
In MongoDB è possibile processare diversi dati per restituire dati aggregati o computazioni
sugli stessi.
> db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATIONS)
È presente il concetto di pipeline nell'aggregazione: possiamo definire una pipeline di stage
consecutivi per ridurre e filtrare i dati all'interno dell'aggregazione. Ad esempio abbiamo gli
stage per:
- $match:
filtra dei dati in base a criteri assegnati
- $group:
raggruppa i dati in base a criteri assegnati
- $unwind:
separa un array del documento creando singoli documenti
- $projection:
proietta i dati in base a criteri assegnati
- $sort:
ordina i dati in base a criteri assegnati
- $limit:
prende in esame solo i primi N document
- $skip:
salta i primi N document
MongoDB
Aggregazione – Group
All'interno di $group possiamo utilizzare diverse espressioni per ottenere dati aggregati o
particolari computazioni sugli stessi:
$sum
$avg
$min
$max
$first
$last
$push
$addtoset
Ad esempio:
db.users.aggregate( [ { $group:{_id:'$name', count_omonimi:{$sum:1} } } ])
db.users.aggregate( [ { $group:{_id:null, eta_min:{$min:'$age'} } } ])
db.users.aggregate( [ { $group:{_id:'$name', eta:{$push:'$age'} } } ])
db.users.aggregate( [ { $group:{_id:'$name', eta:{$addToSet:'$age'} } } ])
MongoDB
Aggregazione – Esempio
db.orders.aggregate( [ {
$match: {status: 'A' } },
{
$group: {_id: '$cust_id',
total: { $sum: '$amount' }
}
} ] )
{ cust_id: 'A123',
amount: 500, status: 'A'
{ cust_id: 'A123',
amount: 250, status: 'A'
}
}
orders
amount: 500, status: 'A'
}
}
{ cust_id: 'A123',
}
{ cust_id: 'B212',
amount: 200, status: 'A'
{ cust_id: 'A123',
amount: 300, status: 'D'
$match
amount: 250, status: 'A'
{ cust_id: 'B212',
amount: 200, status: 'A'
{ cust_id: 'A123',
}
$group
{ _id: 'A123',
total: 750
}
{ _id: 'B212',
total: 200 }
}
results
MongoDB
Funzionalità Avanzate – MapReduce
In MongoDB è presente una implementazione interna del Map-Reduce per la
realizzazione di operazioni di aggregazioni complesse: si possono definire le operazioni
di map e di reduce creando delle funzioni in linguaggio JavaScript.
db.runCommand(
{
opzionali
)
}
mapReduce: <collection>,
map: <function>,
reduce: <function>,
finalize: <function>,
out: <output>,
query: <document>,
sort: <document>,
limit: <number>,
scope: <document>,
jsMode: <boolean>,
verbose: <boolean>
JavaScript function
MongoDB
Funzionalità Avanzate – MapReduce
db.orders.mapReduce(
function( ) { emit ( this.cust_id, this.amount ); },
function( key, values ) { return Array.sum( values ) },
{
query: { status: 'A' },
out: 'order_totals'
})
{ cust_id: 'A123',
amount: 500, status: 'A'
{ cust_id: 'A123',
amount: 250, status: 'A'
}
}
orders
amount: 500, status: 'A'
}
}
{ cust_id: “A123”,
}
{ cust_id: “B212”,
amount: 200, status: 'A'
{ cust_id: 'A123',
amount: 300, status: 'D'
query
amount: 250, status: 'A'
{ cust_id: 'B212',
amount: 200, status: 'A'
{ cust_id: 'A123',
}
}
reduce
map
{ 'A123': [500, 250] }
{ 'B212': 200 }
{ _id: 'A123',
total: 750
}
{ _id: 'B212',
total: 200 }
order_totals
MongoDB
Funzionalità Avanzate – MapReduce
1
definisce una funzione di map, di reduce,
client
una query e una collection di output
2
request
client
mongos
MongoDB
Funzionalità Avanzate – MapReduce
3
Config
mongos
MR request
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Primary
Mongod: Primary
Shard N
Mongod: Primary
Shard N
Shard N
4
query
Mongod: Primary
map
MongoDB
Funzionalità Avanzate – MapReduce
5
map
<K,V>
<k,v> <k,v>
buffer
5a
Mongod: Secondary
<k,v> <k,v> <k,v> <k,v> <k,v>
buffer
partial
reduce
Mongod: Secondary
Mongod: Primary
Shard N
MongoDB
Funzionalità Avanzate – MapReduce
6
reduce
reduce
Mongod
reduce
Mongod
Mongod
7
mongos
Out Collection
Request
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Secondary
Mongod: Primary
Mongod: Primary
Shard N
Mongod: Primary
Shard N
Shard N
MongoDB
Funzionalità Avanzate – MapReduce
8
Mongod: Secondary
Mongod: Secondary
reduce
Mongod: Primary
Shard X
8a
Mongod: Primary
finalize
MongoDB
Funzionalità Avanzate – MapReduce
Il Map Reduce implementato all'interno di Mongo presenta una serie di limiti:
- Javascript non è il linguaggio più indicato per il processamento di funzioni quali MapReduce
- Javascript limita l'utilizzo di librerie di terze parti per l'elaborazione dei dati
- Viene aggiunto un carico extra in termini di informazioni memorizzate
- Solo l'architettura con i shards consente di parallelizzare i dati e solo a seconda della query
che viene utilizzata
Quando i dati crescono occorre passare a un tool pensato per la gestione dei JOB MapReduce
MongoDB
Connettore Hadoop
Lo scopo di questo connettore è offrire la possibilità di usare MongoDB come sorgente
di input o destinazione di output per i task di MapReduce sotto Hadoop.
Permette di utilizzare nei tool dell'universo Hadoop, come Pig ed Hive, i documenti
BSON/MongoDB.
Generalmente il connettore MongoDB viene utilizzato come strato intermedio fra
l'applicazione e Hadoop in modo da sfruttare le potenzialità del query language di
MongoDB per filtrare i dati.
MongoDB
Connettore Hadoop – Aggregazione Batch
MongoDB
Connettore Hadoop – Data Warehouse
MongoDB
Connettore Hadoop – ETL
MongoDB
Connettore Hadoop – Casi d'uso
Gestione dati e metadati
Gestione
dati,
check
Analisi utenti
in,
Analisi utenti, segmentazione,
review
personalizzazione
Gestione dati hotel e
Segmentazione hotel per
prezzi
migliorare le ricerche
MongoDB
Connettore Hadoop – Casi d'uso
Gestione dati studenti
Analisi
studenti
per
l'apprendimento mirato
Gestione dati
Call center / Portali web
Modellazione rischio, sicurezza
e prevenzione truffe
Analisi uso da parte degli
utenti e ottimizzazione
delle tariffe