Curso MongoDB
Curso MongoDB
Curso MongoDB
MONGODB
¡HOLA!
Soy Pablo Campos
Co-founder y CTO en Secmotic Innovation
(@secmotic) y un loco de la innovación y las
nuevas tecnologías.
@PabGallagher
1. Primero lo
INTRODUCCIÓN primero
EL CONCEPTO NOSQL
▸ No solo SQL
▸ Escalabilidad
▸ Versatilidad
▸ Agilidad
▸ Velocidad
CONTEXTUALIZANDO SQL Y
NOSQL
NoSQL SQL
Colección Tabla
Documento Fila
Campos Columna
ORIGEN Y EVOLUCIÓN DE MONGO
▸ Alto rendimiento
▸ Lenguaje de consultas avanzado
▸ Alta disponibilidad
▸ Escalabilidad horizontal
▸ Consola en JS
INSTALANDO MONGODB
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv
EA312927
Colección Tabla
Documento Fila
Campos Columna
BASES DE DATOS
db.createCollection() db.ejemplo.insert()
En este caso definimos Para este caso
el nombre de la directamente damos
colección así como un nombre ejemplo a la
ciertas propiedades al colección e
crearla insertamos un
documento
OPCIONES PARA LA CREACIÓN DE
COLECCIONES
db.articulos.insert({
Título: ‘Aprendiendo MongoDB',
Contenido : ‘Este es el contenido del post',
Tags : [‘eLearn' , 'MongoDB']
});
LOS DOCUMENTOS - READ
MongoDB permite
▸ Proyectar solo algunos de los campos de
un documento
▸ Aplicar expresiones regulares en nuestras
búsquedas
▸ Ordenar la búsqueda
▸ Limitar el número de resultados
▸ “Clarificar” la salida
LOS DOCUMENTOS - READ
use openWebinars
db.articulos.find()
db.articulos.find({},{titulo:1, _id:0})
db.artigulos.find({titulo:{$regex:”.*ngod*.”}})
db.articulos.find().sort({titulo:1}) *o -1
db.articulos.find().limit(5)
db.articulos.find().pretty()
LOS DOCUMENTOS - DELETE
db.articulos.drop()
db.articulos.remove()
LOS DOCUMENTOS - UPDATE
db.articulos.update({
título:‘Aprendiendo MongoDB',
},{
$push: {tags :{$each:[‘eLearn' , 'MongoDB‘, ‘mongo’]}}});
db.artículos.update({
título:‘Aprendiendo MongoDB',
},{
$push: {tags:’bases de datos’}});
db.artículos.update({
título:‘Aprendiendo MongoDB',
},{
$set: {comentarios:’23’}});
EL MÉTODO SAVE
Db.artículos.save({
_id: ObjectId(`Número apuntado`),
título: ‘Aprendiendo a usar save',
Contenido : ‘En este curso vamos a aprender las funcionalidades
básicas de mongoDB',
tags : [‘eLearn' , 'MongoDB']
})
El conocimiento
es una
3. herramienta, y
como todas las
herramientas, su
USUARIOS Y impacto está en
manos del
ROLES usuario.
LA SEGURIDAD EN MONGODB
db.grantRolesToUser(
"reportsUser",
[
{ role: "read", db: "accounts" }
]
)
db.changeUserPassword("reporting", "SOh3TbYhxuLiW8ypJPxmt1oOfL")
USUARIOS Y ROLES - SEGURIDAD
BÁSICA
▸ KISS - Keep It Simple Stupid!
▸ La seguridad es tan fuerte como lo sea el eslabón
más débil de la cadena
▸ Mantener siempre los mínimos permisos posibles
para la operativa normal de cada usuario
4.
DISEÑO Y
ARQUITECTURA
A MONGO LE
VALE TODO
Una de las ventajas de MongoDB es la agilidad
y flexibilidad
TIPOS DE DATOS
▸ String ▸ Null
▸ Integer ▸ Array
▸ Boolean ▸ Object
▸ Double ▸ ObjectId
▸ Timestamp ▸ Binarios
▸ Date ▸ Javascript
¿EMBEBER O REFERENCIAR?
Articulo = {
_id: ‘ID_DEL_ARTICULO_1’,
título: ‘Relaciones 1 a n en SQL para NoSQL’,
Contenido: ‘Vamos a hacer una relación 1 a n en mongoDB’,
tags: [‘Relaciones’, ‘1 a n’],
autor: {
usuario: ‘pcampos’,
fechaDeRegistro: ’10/10/10’,
articulosEscritos: ‘16’
}
}
DE SQL A NOSQL - 1 A N
Queremos relacionar un artículo del blog a sus
comentarios:
Articulo = {
_id: ‘ID_DEL_ARTICULO_1’,
titulo: ‘Relaciones 1 a n en SQL para NoSQL’,
Contenido: ‘Vamos a hacer una relación 1 a n en mongoDB’,
tags: [‘Relaciones’, ‘1 a n’],
comentarios: [{
usuario: ‘pcampos’,
comentario: ‘Gran blog!’
},{
usuario: ‘smario’,
comentario: ‘Mamma mia!’
},{
usuario: ‘Paco’,
comentario: ‘El link está roto!’
}]
}
DE SQL A NOSQL - 1 A N
Queremos relacionar un artículo del blog a sus
comentarios:
articulo = {
_id: ‘ID_DEL_ARTICULO_1’,
titulo: ‘Relaciones 1 a n en SQL para NoSQL’,
Contenido: ‘Vamos a hacer una relación 1 a n en mongoDB’,
tags: [‘Relaciones’, ‘1 a n’],
comentarios: [‘ID_COMENTARIO_1’,
’ ID_COMENTARIO_2’,
’ ID_COMENTARIO_3’]}
comentario = {
_id: ‘ID_DEL_COMENTARIO’,
idDelArticulo: ‘ID_DEL_ARTICULO_1’,
usuario: ‘pcampos’,
comentario: ‘Buen articulo!’}
DE SQL A NOSQL - N A N
Queremos relacionar varios artículos del blog a sus
coautores:
Articulo = {
_id: ‘ID_DEL_ARTICULO_1’,
titulo: ‘Relaciones 1 a n en SQL para NoSQL’,
Contenido: ‘Vamos a hacer una relación 1 a n en mongoDB’,
tags: [‘Relaciones’, ‘1 a n’],
autores: [‘ID_DEL_AUTOR_1’, ‘ID_DEL_AUTOR_2’, ‘ID_DEL_AUTOR_3’]
}
Autor = {
_id: ‘ID_DEL_AUTOR_1’,
usuario: ‘pcampos,
artículos: [‘ID_DEL_ARTICULO_1’, ‘ID_DEL_ARTICULO_2’]
}
CONSIDERACIONES GENERALES
▸ Añade a un documento toda la información que
puedas, ahorrarás “JOINS”
▸ Si un subdocumento se repite en nuestra aplicación,
es mejor referenciar
▸ Si una lista de subdocumentos va a crecer
indiscriminadamente, mejor referenciar
▸ Los documentos están limitados a 16MB
▸ Si se anida mucha información en un mismo
documento, la búsqueda es más ineficiente
▸ La atomicidad de las operaciones es a nivel de
documento en MongoDB
CONSIDERACIONES GENERALES
PRE-LOCALIZACIÓN
▸ Cuando conocemos la estructura final que tendrá
un documento (time-series) podemos pre-localizar el
documento y evitar la relocalización en disco de la
información. Únicamente será necesario actualizar
nuestro documento.
{
timestamp_hour: ISODate("2013-10-10T23:00:00.000Z"),
type: “memory_used”,
values: {
0: { 0: 999999, 1: 999999, …, 59: 1000000 },
1: { 0: 2000000, 1: 2000000, …, 59: 1000000 },
…,
58: { 0: 1600000, 1: 1200000, …, 59: 1100000 },
59: { 0: 1300000, 1: 1400000, …, 59: 1500000 }}}
CONSIDERACIONES GENERALES
ATOMICIDAD
▸ Todas las operaciones en mongo son atómicas a
nivel de documento
▸ Si es necesario modificar un documento y otro
documento al que este haga referencia, las
operaciones deben hacerse por separado (two phase
commit)
▸ Siempre que sea posible debe guardarse toda la
información relevante y sujeta de ser modificada
dentro de un documento, si la aplicación soporta
actualizaciones no atómicas, la información puede
separarse en dos documentos
CONSIDERACIONES GENERALES
COLECCIONES VOLÁTILES
▸ Índices TTL -> se explicarán en el módulo de índices
Capped collections -> Colecciones con un tamaño
máximo definido
▸ Mantienen el orden de inserción
Funciona como una cola FIFO
Si van a actualizarse documentos en una capped
collection, es necesario definir un índice para no
escanear la colección entera
No pueden “shardearse”
db.developers.find().sort({ horas : -1 })
db.developers.find().sort({ horas : -1 }).hint({ horas : 1 })
¿QUÉ PROPIEDADES TIENEN LOS
ÍNDICES? - ÍNDICES PARCIALES
▸ Se define una condición y solo se indexa la parte de
la muestra que la cumpla
▸ Es incompatible con la propiedad sparse
db.delanteros.createIndex({
nombre : 1, equipo : 1 },{
partialFilterExpression : { goles : { $gt : 10 } }})
Artículo = {
_id: ‘ID_DEL_ARTICULO_1’,
titulo: ‘Relaciones 1 a n en SQL para NoSQL’,
Contenido: ‘Vamos a hacer una relación 1 a n en mongoDB’,
tags: [‘Relaciones’, ‘1 a n’],
comentarios: [{
usuario: ‘pcampos’,
comentario: ‘Gran blog!’},
{ usuario: ‘smario’,
comentario: ‘Mamma mia!’},
{ usuario: ‘Paco’,
comentario: ‘El link está roto!’
}],
shares: 19}
ÍNDICES SIMPLES
Para crear un índice usamos el comando ensureIndex
db.artículos.ensureIndex({ shares : 1 })
db.artículos.find({stock:20}).sort({udsVendidas:1})
ÍNDICES MULTILLAVE
▸ Para crear un índice usamos el comando
ensureIndex
▸ Puede construirse sobre arrays que contengan
cadenas, números u otros documentos
▸ No es posible construir un índice compuesto de
índices multillave, pero si un índice compuesto de
uno simple y uno multillave
db.artículos.ensureIndex({ tags : 1 })
db.artículos.find({tags:’eLearn’})
ÍNDICES MULTILLAVE
▸ Pueden realizarse queries para buscar un array
específico pero el índice multillave no será usado en
su totalidad
db.películas.find( { ratings: [ 5, 9 ] } )
{ _id: ID,
item: “NOMBRE",
stock: [
{ size: “TALLA", color: “COLOR", quantity: CANTIDAD },
{ size: “TALLA", color: “COLOR", quantity: CANTIDAD },
{ size: “TALLA", color: “COLOR", quantity: CANTIDAD }, ]}
ÍNDICES DE TEXTO
▸ Facilitan y optimizan las búsquedas basadas en
texto en gran medida
▸ Se declaran igual que el resto de índices con la
particularidad de no ponerse a uno el valor, si no a
“text”
▸ Pueden indexarse varios campos con un índice de
texto
▸ Solo puede declararse un índice text por colección
db.artículos.createIndex({ titulo : “text” })
db.artículos.createIndex({
titulo : “text”, contenido : “text”
},{
weights: { titulo : 10, contenido : 5 },
name: “TextIndex”
})
ÍNDICES DE TEXTO
Si se quiere indexar todo el contenido de texto de una
colección se emplea el wildcard specifier.
Pueden declararse pesos para cualquier campo
indexado
db.artículos.createIndex({
contenido : “text”
},{
language_override: “idioma”})
ÍNDICES DE TEXTO
▸ La tokenización de las palabras está resuelta incluso
con símbolos (desde la versión 3 de mongo)
▸ Mongo cuenta con varios lenguajes predefinidos, así
como stop words
▸ Los índices de texto son sparse por defecto
{
$text:
{
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>
}
}
ÍNDICES DE TEXTO
▸ Buscar una palabra
▸ Buscar por varias palabras
▸ Buscar una frase
▸ Buscar por una palabra excluyendo otra
▸ Buscar por idioma
▸ Buscar con sensibilidad a mayúsculas y minúsculas
▸ Buscar y recuperar la puntuación del resultado
ÍNDICES DE TEXTO
Si son tan útiles, ¿por qué no empleamos siempre la
wildcard e indexamos todo?
▸ Son extensos
▸ Son lentos de construir
▸ Son costosos en memoria
▸ La búsqueda de frases es más efectiva sin índices
ÍNDICES - ALGO MÁS SOBRE ELLOS
▸ Para no dejar sin servicio a la aplicación, cuando
construir un índice vaya a ser muy costoso, hacerlo
en background
db.COLECCION.createIndex( { CAMPO: 1}, {background: true} )
¿Qué índices usarías para buscar tipo food de entre 100 y 300 de calidad?¿Por qué?¿Estabas
en lo cierto?
6.
AGREGACIÓN
AGREGACIÓN
▸ La agregación permite realizar queries “anidadas”
que incluyan operaciones
▸ Se desarrollan como la tunelación el Linux,
siguiendo el orden especificado
▸ Si se usa MongoDB como motor de base de datos, es
más eficiente emplear los métodos de agregación
por tunelación que mapReduce
▸ Map-Reduce se ejecuta partido en dos fases
▹ Mapeo de todos los documentos para emitir los
objetos deseados a la siguiente fase
▹ Fase de “reducción” que se encarga de operar
con los datos recibidos de la primera fase
AGREGACIÓN
▸ Operaciones de agregación simple
▹ Count: Devuelve un entero que indica el
número de documentos que cumplesn con la
query realizada
▹ Group: Agrupa todos los documentos de una
colección según el campo que se indique… Igual
que GROUP BY
▹ Distinct: Devuelve los distintos campos únicos
que existan para el que se especifique
db.COLECCION.count(QUERY)
db.COLECCION.distinct(“CAMPO”)
db.COLECCION.group{key, reduce, initial} ()
AGREGACIÓN
▸ Mongo aprovecha los índices durante las
operaciones de agregación para los operadores
$match y $sort
▸ MongoDB optimiza solo las operaciones de
agregación para que sean más eficientes cuando
detecta un error común
OPERACIONES
▸ $project -> Proyecta como salida los campos
especificados de todos los obtenidos en la query
anterior
▸ $match -> Filtra documentos según el criterio que
se especifique, como en un find()
▸ $limit -> limita los resultado al número de
documentos especificado
▸ $skip -> Se salta el número de documentos
indicados del resultado de la operación anterior
OPERACIONES
▸ $unwind -> Deconstruye un el array de un
documento y construye muchos documentos que
tienen un solo valor en el campo en el que había un
array
▸ $group -> Agrupa el resultado en base al campo que
se indique
▸ $sort -> Ordena los resultados
▸ $lookup ->Permitie realizar operaciones JOIN SQL
en base de datos mongoDB
OPERACIONES
▸ $out -> Debe ser el último operador y permite
especificar una colección en la que guardar el
resultado obtenido
▸ $and, $or y $not -> Operadores booleanos
▸ $cmp -> 0 si los dos valores son iguales y 1 en caso
contrario
▸ $eq -> true si los valores son equivalentes
▸ $gt, $gte, $lt, $lte, $ne -> Mayor que, mayor que o
igual…
▸ $in, $nin -> contenido en, no contenido en
OPERACIONES
▸ $sum -> Suma los valores numéricos del campo
especificado (usarlo en group)
▸ $avg -> Valor medio del campo especificado (group)
▸ $first, $last, $max, $min
▸ Operadores aritméticos: $divide, $sqrt, $exp…
▸ Operadores de fechas: $week, $hour, $year…
OPTIMIZACIÓN
▸ $match siempre antes que $sort para minimizar el
número de objetos que ordenar
▸ Cuando ponemos $skip antes de $limit, Mongo los
reordena al revés (cambiando los valores
$limit=$limit + $skip) para permitir la fusión de los
comandos $sort y $limit
▸ Cuando Mongo detecta que dos comandos pueden
fusionarse, lo hace automáticamente de manera
interna y transparente
OPTIMIZACIÓN
▸ Cuando ponemos $project seguido de $skip o $limit,
mongo adelanta a los segundos para que sea una
proyección de menos resultados o para que si hay
un $sort delante pueda fusionarse
▸ Mongo fusiona dos $limit seguidos
▸ Mongo fusiona dos $skip seguidos
OPTIMIZACIÓN
▸ Cuando ponemos dos $match seguidos, es más
eficaz realizar la misma operación en uno solo
contando con el operador lógico $and
▸ Cuando ejecutemos un $lookup seguido de un
$unwind podemos optimizarlo poniendo el $unwind
en el propio $lookup
{
{
$lookup: {
$lookup: {
from: "otherCollection",
from: "otherCollection",
as: "resultingArray",
as: "resultingArray",
localField: "x",
localField: "x",
foreignField: "y",
foreignField: "y"
unwinding: { preserveNullAndEmptyArrays:
}
false }
},
}
{ $unwind: "$resultingArray"}
}
MAR-REDUCE
▸ Menos eficiente que aggregate()
▸ Se define la fase de mapeo, la de reducción, la
posible query y la posible colección de salida para el
resultado
db.pedidos.mapReduce(
function(){ emit( this._id, this.precio ) },
function(key, values ){ return Array.sum( value ) },
{
query: { categoría : “limpieza” },
out: “totales”
}
)
7.
RÉPLICA SETS
REPLICA SET
▸ Provee “sólo” de redundancia y disponibilidad
▸ Nada de balanceo de carga
▸ Es un grupo de instancias mongod configuradas
para replicar el contenido de determinadas bases de
datos según las entradas del log de un servidor
primario
REPLICA SET
▸ Solo el nodo primario recibe las operaciones de
escritura
▸ Los nodos secundarios replican las operaciones que
realiza el nodo primario cuando este las anota en el
log de operaciones de mongo
▸ Si un nodo primario cae, los nodos secundarios
deciden quien juega el papel de primario desde ese
momento
REPLICA SET
▸ Pueden definirse instancias de mongod como
árbitros que no replican información, únicamente
tienen voto a la hora de elegir un nuevo nodo
primario
▸ Los nodos árbitro nunca cambian de estado hacia
otro tipo de nodo
REPLICA SET
▸ Si el nodo primario del cluster se cae o pierde la
comunicación durante 10 segundos, se le considera
caído y se pasa a buscar un nuevo nodo primario
▸ El proceso de caída/reelección dura
aproximadamente 1 minuto
▹ 10-30 segundos caída del nodo primario y
comunicación de la misma
▹ 10-30 segundos elecciones
REPLICA SET
▸ Por defecto se realizan las operaciones de lectura
sobre el nodo primario
▸ Puede configurarse un cluster con un readConcern
que especifique que se lea de nodos secundarios
cuando sea necesario
▹ Primary
▹ primaryPreferred
▹ Secondary
▹ secondaryPreferred
▹ Nearest
▸ CUIDADO con la replicación asíncrona
db.getMongo().setReadPref('primaryPreferred')
REPLICA SET
▸ Miembros con prioridad 0: sets de réplica
secundarios que no pueden ser elegidos como
primarios
▹ Aceptan operaciones de lectura
▹ Mantienen una copia de los datos
▹ Votan en las elecciones
▹ No pueden ser elegidos
▹ No pueden lanzar unas elecciones
▹ Pueden configurarse en stand-by
cfg = rs.conf()
cfg.members[2].priority = 0
rs.reconfig(cfg)
REPLICA SET
▸ Miembros ocultos: sets de réplica secundarios que
no pueden ser elegidos como primarios ni recibir
operaciones de lectura
▹ No aceptan operaciones de lectura
▹ Mantienen una copia de los datos
▹ Votan en las elecciones
▹ No pueden ser elegidos
▹ No pueden lanzar unas elecciones
cfg = rs.conf()
cfg.members[0].priority = 0
cfg.members[0].hidden = true
rs.reconfig(cfg)
REPLICA SET
▸ Miembros “retrasados”: sets de réplica secundarios que guardan el estado
del nodo primario con un retraso de tiempo definido
▹ No aceptan operaciones de lectura
▹ Mantienen una copia de los datos
▹ Votan en las elecciones si se especifica
▹ No pueden ser elegidos
▹ No pueden lanzar unas elecciones
▹ Deben ser hidden y priority 0
▹ Su “retraso” debe ser más pequeño que la capacidad del opLog
{
"_id" : <num>,
"host" : <hostname:port>,
"priority" : 0,
"slaveDelay" : <seconds>,
"hidden" : true
}
REPLICA SET
▸ El log de operaciones es una capped collection que
mantiene un registro de las operaciones de la BBDD
▸ 5% de la capacidad libre del disco {990MB-50GB}
▸ CUIDADO con las actualizaciones “masivas”
▸ CUIDADO con hacer muchas pequeñas
actualizaciones
rs.printReplicationInfo()
REPLICA SET
▸ Sincronización inicial
▹ Clona todas las bases de datos y construye los índices _id
▹ Refleja los nuevos cambios del opLog a la nueva instancia
▹ Construye el resto de índices
▹ La instancia pasa a ser una instancia secundaria
▸ Se sincronizan las réplicas por batches
▸ Los miembros secundarios que tengan voto nunca copian de un
miembro que no tenga voto
▸ Los miembros secundarios nunca copian de un miembro
“retrasado”
▸ Pueden darse rollbacks de miembros primarios que reconectan
tras una caída
REPLICA SET
▸ Un set de réplicas sólo puede tener 7 miembros con
voto
▸ Un set de réplicas sólo puede tener 50 miembros en
total
▸ SIEMPRE es recomendable configurar un número
impar de miembros con voto para evitar los empates
▹ En caso de tener un número par de votantes,
mejor añadir un miembro árbitro (No requiere
mucho espacio)
REPLICA SET
▸ Considerar la tolerancia al fallo
▹ Diferencia entre el número de miembros y el
número de votos requeridos para elegir un
primario
▸ Balancea la carga de lecturas si el número de
operaciones es alto
db.runCommand({movePrimary:<databaseName>,to:<newPrimaryShard>})
sh.status()
SERVIDORES DE CONFIGURACIÓN
▸ Los servidores de configuración almacenan metadatos
relativo a los chunks almacenados en cada shard así como
la distribución de shard keys
▸ Cada sharded cluster requiere de su propio servidor de
configuración, no puede definirse uno genérico
▸ Se escribe información en ellos cuando se produce una
migración o una división de chunks
▸ Se lee de ellos cuando se inicia un nuevo mongos (por
primera vez o restart) y cuando cambian sus metadatos
▸ Los metadatos se almacenan en la base de datos config
MONGOS
▸ Se encarga de enrutar las queries de lectura/escritura al
shard que corresponda
▸ Los router cachean los metadatos acerca de la
distribución de los chunk sobre los shards para enrutar
▸ Primero determina los shard que deben recibir la query y
después establece un cursor apuntando al resultado de
cada query
▸ Operaciones como la agregación o la ordenación se
ejecutan en el primary shard (u otro) antes de devolver el
resultado completo
db.isMaster()
PUESTA EN MARCHA
▸ Se define una shard key con el siguiente comando:
sh.shardCollection( namespace, key )