Tudo o que você precisa saberFGSL - 2014
Christiano [email protected]://christiano.meTwitter: @dump
Quem sou?
● Arquiteto de dados na Propus;● Trabalho com web e software livre
desde 1995;● Python desde 2000;● MongoDB desde o início do projeto;● Colaboro e já colaborei com
projetos como:– GNU Project (Free Software
Foundation);
– Debian Project;
– Python;
– MongoDB – MUG - SP;
● Twitter: @dump● Blog:
http://christiano.me● Facebook, LinkedIn:
Christiano Anderson
Mongo?
● Sim, em muitos idiomas pode ser um termo pejorativo, mas a origem vem de:
Humongous
“Gigantesco”
História
● Foi criado pelos fundadores da Doubleclick;● 10gen foi fundada em 2007;● A ideia inicial era fazer um produto semelhante
ao Google App Engine;
Alta curva de crescimento
Contribuições ao core do MongoDB
Grandes players utilizando
● Foursquare;● Github;● EA Games;● Entre diversos outros
– No Brasil:● EasyTaxi;● Globo.com;● IG;● Mercado Livre● ZAP Imóveis● Ingresse.com● Terra Networks
Um pouco de conceitos...
● NoSQL: O termo foi criado por Carlo Strozzi e Eric Evans como referência a um tipo de armazenamento de dados;
● Nunca, mas nunca está relacionado a ódio ao modelo SQL, pelo contrário, podem até trabalhar em conjunto;
● O termo NoREL e Não Relacional também é bastante utilizado
Por que usar NoSQL?
● Novos paradigmas (nem tão novos assim);● Funcionalidades;● Escalabilidade;● Performance;● Não ficar preso a modelagem;
Volume de dados
● Grande volume é relativo, o que você considera grande?– Dados que crescem exponencialmente;
– Agregam muitos valores dinamicamente;
– Não precisam de modelagem;
Considere uso de MongoDB se...
● Está usando muito cache em sua aplicação;● Os dados mudam muito;● Os dados estão crescendo de forma exponencial;● Precisa de processamento em tempo real;● Gosta de desenvolvimento ágil;● Sua aplicação é “beta perpétua”;● Tem dificuldade para trabalhar com modelo relacional;● Usa muito “join” na sua aplicação relacional;
Iniciando com MongoDB
● Sua distribuição GNU/Linux deve possuir pacotes prontos;
● No site da MongoDB, possível baixar binários para outros sistemas operacionais;
● A instalação é bem simples, a configuração padrão do MongoDB já atende quase todos os cenários;
Pode substituir o banco relacional?
● Até pode, mas é uma questão de arquitetura e escolhas;
● Uma aplicação pode usar MongoDB e banco relacional;
● Tudo vai depender da sua arquitetura;
Não existe a melhor ferramenta...
… Existe a que atende melhor a sua necessidade. A necessidade pode exigir mais de uma ferramenta.
Suporte a linguagens de programação
● Praticamente todas as linguagens de programação possuem suporte (driver) para MongoDB;
● Suporte oficial às principais linguagens (Python, C, C++, PHP, Java, NodeJS, Perl, Scala, Ruby, C#);
● Suporte da comunidade a diversas outras linguagens (R, Go, Erlang, LISP, Lua, Matlab, Smalltalk, entre outras)
Recursos animais!
● Busca textual (Full Text Search);● Aggregation framework;● Índices espaciais (geográficos);● Sharding;● Replica Set;
Busca textual
● Possui suporte a português do Brasil;● Stemming;● Stopwords;
Stemming
● Se a frase abaixo estiver indexada como FTS:
“Enquanto houver vontade de lutar, haverá esperança de vencer”
● Se houver uma busca pela palavra “vencendo”, a mesma será exibida no resultado de busca.
Interface em JavaScript
● O MongoShell é baseado em JavaScript, oferece toda flexibilidade para gerenciar o banco de dados e executar operações administrativas
Nomenclaturas
Banco Relacional MongoDB
Base de dados --> Base de Dados
Tabela --> Coleção
Registro --> Documento
Índice --> Índice
Join --> Documento embarcado
Foreign key --> Referência
Modelo de documento
{'nome':'Christiano',
'sobrenome':'Anderson',
'email':'[email protected]',
'twitter':'@dump',
'blog':'http://christiano.me',
'idade': 35,
'palestrante': true}
Realizando operações via MongoShell
● O MongoDB é implícito, não existe necessidade de criar toda estrutura do banco de dados antes;
● O MongoShell é uma ótima forma de aprendizado!
MongoShell
anderson@endor:~$ mongoMongoDB shell version: 2.4.6connecting to: test> a = 1010> b = 3030> a < btrue> b < afalse
Vamos lá...
anderson@endor:~$ mongoMongoDB shell version: 2.4.6connecting to: test> use escolaswitched to db escola>
Nesse ponto, o bancoainda está vazio.
Inserindo um registro
anderson@endor:~$ mongoMongoDB shell version: 2.4.6connecting to: test> use escolaswitched to db escola> db.alunos.insert({... 'nome':'Rolando Rocha',... 'turma':'Python',... 'nota': 10})>
Nesse ponto, o banco foi criadoe o documento foi inserido, jáestá persistido em disco
Verificando o registro
> db.alunos.findOne(){
"_id" : ObjectId("525ecd6585512f4130afd2c4"),"nome" : "Rolando Rocha","turma" : "Python","nota" : 10
}
ObjectId é único para cada documento
Inserindo outro registro
> db.alunos.insert({... nome: 'Carolina',... sobrenome: 'Ferreira',... sexo: 'feminino',... idade: 29,... email: '[email protected]',... materias: ['MongoDB','Riak','Java'],... notas: {'MongoDB': 10, 'Riak': 8, 'Java': 9}... })>
Listando apenas o registro da Carolina
> db.alunos.find({'nome':'Carolina'}).pretty(){ "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "[email protected]", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 }}>
Só mais um registro...
> db.alunos.insert({... nome: 'Juliana',... sobrenome: 'Silva',... sexo: 'feminino',... idade: 21,... materias: ['Riak','Python']... })
> db.alunos.count()3
Listando apenas quem é do sexo feminino
> db.alunos.find({sexo:'feminino'})
{ "_id" : ObjectId("5230ee7ec3141857756a81a8"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "[email protected]", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }{ "_id" : ObjectId("5230eec6c3141857756a81a9"), "nome" : "Juliana", "sobrenome" : "Silva", "sexo" : "feminino", "idade" : 21, "materias" : [ "Riak", "Python" ] }
Listando apenas quem tem MongoDB na matéria
> db.alunos.find({materias:'MongoDB'})
{ "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "[email protected]", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }>
Listando quem tem menos de 30 anos
> db.alunos.find({idade: {$lt: 30} })
{ "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "[email protected]", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }{ "_id" : ObjectId("522f264c8434c181910716ed"), "nome" : "Juliana", "sobrenome" : "Silva", "sexo" : "feminino", "idade" : 21, "materias" : [ "Riak", "Python" ] }>
Alguns operadores de consulta
Operador Descrição
$gt Maior que
$gte Maior ou igual que
$lt Menor que
$lte Menor ou igual que
Atualização de registro
> db.alunos.update({'nome':'Carolina'}, {$set: {'sobrenome':'Ferreira Martins'}})
Padrão de pesquisa
Operador de alteração
Resultado> db.alunos.find({'nome':'Carolina'}).pretty(){ "_id" : ObjectId("522f2b998434c181910716ee"), "email" : "[email protected]", "idade" : 29, "materias" : [ "MongoDB", "Riak", "Java" ], "nome" : "Carolina", "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 }, "sexo" : "feminino", "sobrenome" : "Ferreira Martins"}>
O que acontece se fizer isso?
> db.alunos.update({'nome':'Carolina'}, {'sobrenome':'Ferreira Martins'})
Removendo registros
db.alunos.remove({'sobrenome':'Ferreira Martins'})
Listando só o nome dos alunos
> db.alunos.find({},{'nome':true, '_id': false})
{ "nome" : "Rolando" }{ "nome" : "Juliana" }{ "nome" : "Carolina" }
Ordenação
> db.alunos.find({}, {'nome': true, '_id': false}).sort({nome: 1})
{ "nome" : "Carolina" }{ "nome" : "Juliana" }{ "nome" : "Rolando" }
Comparativo SQL
SQL MongoDB
INSERT INTO USERS VALUES(1,1) db.users.insert({a:1, b:1})
SELECT a,b FROM users db.users.find({}, {a: 1, b: 1})
SELECT * FROM users db.users.find()
SELECT * FROM users WHERE age=33 db.users.find({age: 33})
SELECT * FROm users WHERE name = “pedro”
db.users.find({name:”pedro”})
Comparativo SQL
SQL MongoDB
SELECT * FROM users WHERE age=33 ORDER BY name
db.users.find({‘age’:33}).sort({name:1})
SELECT * FROM users WHERE age < 33 db.users.find({‘age’:{$lt:33}})})
CREATE INDEX myindexname ON user(name)
db.users.ensureIndex({name:1})
SELECT * FROM users WHERE a = 1 AND b = ‘q’
db.users.find({a:1, b:’q’})
SELECT * FROM users LIMIT 10 SKIP 20 db.users.find().limit(10).skip(20)
Como descobrir documentos que não possuem determinada chave
> db.alunos.find({'email': {$exists: false} })
Adicionando chave em todos os documentos
> db.alunos.update({ },
{ $set: { 'aprovado': true } },
{ multi: true })
O que adicionar
Query
Grava a alteração em todosOs registros que atendem
Ao critério
Criando apontamentos (relacionamentos)
> db.alunos.find().pretty(){ "_id" : "rolando", "nome" : "Rolando", "email" : "[email protected]" }{ "_id" : "carol", "nome" : "Carol", "email" : "[email protected]" }{ "_id" : "july", "nome" : "Juliana", "email" : "[email protected]" }
> db.biblioteca.find().pretty(){ "_id" : "0001", "titulo" : "Aprenda MongoDB" }{ "_id" : "0002", "titulo" : "Aprenda Python" }{ "_id" : "0003", "titulo" : "Aprenda Shell" }
Primeiro cenário
> db.alunos.update({'_id':'rolando'},{$set:{'biblioteca_id':['0001','0002']}})
> db.alunos.find({'_id':'rolando'}).pretty(){
"_id" : "rolando","biblioteca_id" : [
"0001","0002"
],"email" : "[email protected]","nome" : "Rolando"
}
Explicação primeiro cenário
● Adicionado à coleção de ALUNOS uma chave chamada biblioteca_id;
● Facilita a pesquisa de quais alunos alugaram livros (dentro da coleção alunos);
● Dificulta a pesquisa de quais livros foram alugados (dentro da coleção da biblioteca);
Segundo cenário
> db.biblioteca.update({'_id':'0001'},{$set:{'aluguel':{'aluno_id':'rolando','data':'2013-09-09'}}})
> db.biblioteca.find({'_id':'0001'}).pretty(){
"_id" : "0001","aluguel" : {
"aluno_id" : "rolando","data" : "2013-09-09"
},"titulo" : "Aprenda MongoDB"
}
Explicação segundo cenário
● Adicionada uma chave “aluguel” dentro da coleção biblioteca;
● Todos livros alugados possuem o atributo “aluguel”;● Facilita a busca dos livros disponíveis e alugados;● Pode criar histórico de alugueis;● Mas dificulta a busca de quais alunos estão alugando
livro (visão: coleção de alunos);● Esse seria um cenário recomendado!
Terceiro cenário
● Criar uma collection de referência, exemplo: “aluguel_livros” e relacionar o _id do aluno com _id do livro, assim como qualquer outra informação adicional
NÃO USE ESSE MODELO! É MUITO RELACIONAL!
Aplicação de blog no MongoDB, casamento perfeito!!!
{"_id" : ObjectId("541f6a9092a2ee25fedaa655"),"titulo" : "Aqui é o título","tags" : [
"teste","exemplo","mongodb"
],"conteudo" : "Aqui vem o Lorem Ipsum básico","comentarios" : [
{"usuario" : "Usuario Troll","email" : "[email protected]","comentario" : "Vim aqui só trollar"
},{
"usuario" : "Usuario Sério","email" : "[email protected]","comentario" : "Parabéns pelo post"
}]
}
Exemplo Aplicação Blog
Os comentários ficam embarcados no mesmo documento que o post
Guardar logs, séries temporais e muitos documentos pequenos, também tem uma
modelagem específica para isso.
Pense nas possibilidades de uso desses dados antes de gravar qualquer coisa!
Faça testes, muitos testes
Série temporal
timestamp memoria_usada2013-10-10T23:06:37.000Z 10000002013-10-10T23:06:38.000Z 20000002013-10-10T23:06:39.000Z 2332200
A principal pergunta:
Como você vai buscar esses dados?
Vai agregar por horas, dia, mês?
Documentos únicos
{ timestamp: ISODate("2013-10-10T23:06:37.000Z"), type: ”memory_used”, value: 1000000},
{ timestamp: ISODate("2013-10-10T23:06:38.000Z"), type: ”memory_used”, value: 2000000},
{ timestamp: ISODate("2013-10-10T23:06:39.000Z"), type: ”memory_used”, value: 2322000}
Documentos únicos
● Um dia possui 86.400 segundos;● 86.400 documentos por dia na coleção;● Se quiser pegar o histórico de um dia, terá de
varrer 86.400 documentos;● Se quiser pegar o histórico de um ano, serão
31.556.926 documentos consultados!!!● Isso para o MongoDB é pouco, mas pode ser
otimizado...
Agregado por minuto{ timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"), type: “memory_used”, values: { 0: 999999, … 37: 1000000, 38: 1500000, … 59: 2000000 }}
23:06
Todos os segundos das 23:06
Documentos agregados por minuto
● Um dia possui 1.440 minutos;● 1.440 documentos por dia na coleção;● Se quiser pegar o histórico de um dia, terá de
varrer 1.440 documentos;● Se quiser pegar o histórico de um ano, serão
525.600 documentos consultados!!!● Ainda pode ser mais otimizado...
Agregado por hora{ timestamp_hour: ISODate("2013-10-10T23:00:00.000Z"), type: “memory_used”, values: { 0: 999999, 1: 1000000, …, 3598: 1500000, 3599: 2000000 }}
Cada segundo dessa hora e seus respectivos valores
1h = 3.600 segundos
23h
Documentos agregados por hora
● Um dia possui 24 horas;● 24 documentos por dia na coleção;● Se quiser pegar o histórico de um dia, terá de
varrer apenas 24 documentos;● Se quiser pegar o histórico de um ano, serão
apenas 365 documentos consultados!!!● Chegamos em um nível interessante de
granularidade de dados...
Melhor schema
A pergunta de ouro:
Como buscar o maior número de informações com menor número de queries (de preferência,
uma)?
Melhor schema
● Eficiência na gravação● Eficiência na leitura● Não existe mágica, é necessário entender o
funcionamento da aplicação● Bom schema, bom código = sucesso garantido● Índices e agregadores podem ser necessários.● Pesquise mais sobre Schema Design MongoDB;
Conclusão
● É um novo paradigma, evite pensar de forma relacional, senão o projeto ficará engessado;
● Pode parecer estranho no começo, mas a prática mostra que esse modelo funciona muito bem e é muito produtivo;
Perguntas???
Obrigado!!! Se não deu tempo de responder sua pergunta, me chame nas redes sociais ou pelos corredores do FGSL! :-)
http://christiano.me
Twitter: @dump
Email: [email protected]
Facebook, LinkedIn: Christiano Anderson