Semana passada eu e o Vinícius iniciamos o desenvolvimento de um sistema para ser usado internamente na Improve It e tivemos diversas surpresas com o uso do CouchDB. Basicamente ficamos impressionados como foi rápido evoluir do primeiro ao último protótipo sendo que estruturalmente os dados mudaram muito ao longo do tempo. Isso foi simples graças conceito de documento e por não precisar ficar manipulando esquemas.
O objetivo desse post era escrever sobre como muito mais fácil armazenar objetos no CouchDB simulando o que é feito usando o nested form do Rails. Porém no meio do caminho eu descobri que existem poucos materiais sobre CouchDB em português e resolvi escrever um pequeno post dando uma visão geral sobre o CouchDB e adiar o post original para o futuro.
Apesar de ser apenas um overview não existe muito mais coisa apara mostrar na questão de operações, é um banco muito simples e poderoso. Porém é uma quebra de paradigma e a ficha demora para cair, se é que já caiu na minha cabeça.
O que é o CouchDB?

O CouchDB é um banco de dados orientado a documentos desenvolvido em Erlang.
Erlang é uma linguagem de programação criada pela Ericsson para aplicações distribuídas, tolerante a falhas e com alto poder de concorrência que se tornou opensource em 1998.

O que é um documento para o CouchDB?

O documento que o CouchDB armazena nada mais é que um JSON e não existe necessidade de se definir um esquema previamente.

Exemplo:
{
"_id": "c5fd59bcfb8fd5ef9baa2f6a44924033",
"_rev": "5-2549041723",
"name": "Marcos Tapajós",
"type": "person",
"register_number": 256987456,
"age": 27,
"_attachments": {
"tapa-orkut.jpg": {
"stub": true,
"content_type": "image/jpeg",
"length": 74635
}
}
}
Como é o acesso ao CouchDB?
Todas as operações no CouchDB são feitas via RESTful Web Services. A maior parte das linguagens possuem ferramentas para lidar com Web Services que simplificam bastante o uso da API do CouchDB, embora seja extremamente simples lidar com essa API.
Existem quatro operações básicas (PUT, POST, GET, DELETE) para manipular documentos no CouchDB.
? Create: HTTP PUT /database/document_id
? Create: HTTP POST /database
? Read: HTTP GET /database/document_id
? Update: HTTP PUT /database/document_id
? Delete: HTTP DELETE /database/document_id
Manipulando documentos no CouchDB
Vejamos alguns exemplos de operações executadas em um terminal Unix:
Criando um banco:
curl -X PUT http://127.0.0.1:5984/addresses
{"ok":true}
Listando todos os bancos
curl -X GET http://127.0.0.1:5984/_all_dbs
["addresses","beonthenet"]
Apagando um banco:
curl -X DELETE http://127.0.0.1:5984/beonthenet
{"ok":true}
Buscando um documento:
curl -X GET http://127.0.0.1:5984/addresses/c5fd59bcfb8fd5ef9baa2f6a44924033
{"_id":"c5fd59bcfb8fd5ef9baa2f6a44924033","_rev":"5-2549041723","name":"Marcos Tapajós","type":"person","register_number":256987456,"age":27,"_attachments":{"tapa-orkut.jpg":{"stub":true,"content_type":"image/jpeg","length":74635}}}
Criando um documento especificando um ID:
curl -X PUT -d '{"name":"Kent Beck", "age":58}' http://127.0.0.1:5984/addresses/kent-beck
{"ok":true,"id":"kent-beck","rev":"1-1914729463"}
Criando um documento sem especificar um ID:
curl -X POST -d '{"name":"Kent Beck", "age":58}' http://127.0.0.1:5984/addresses
{"ok":true,"id":"12920ea0c035185f767ee3432983e1dc","rev":"1-1023121281"}
Atualizando um documento:
curl -X PUT -d '{"_id":"kent-beck","_rev":"1-2296068035","name":"Kent Beck","age":58,"roundhousekick":false}' http://127.0.0.1:5984/addresses/kent-beck
{"ok":true,"id":"kent-beck","rev":"2-3170766222"}
Apagando um documento:
curl -X DELETE http://127.0.0.1:5984/addresses/kent-beck?rev=2-3170766222
{"ok":true,"id":"kent-beck","rev":"3-1347458588"}
curl -X GET http://127.0.0.1:5984/addresses/kent-beck
{"error":"not_found","reason":"deleted"}
Analisando esses exemplos de consultas vemos que as consultas que fizemos ao CouchDB retornaram um parâmetro chamado "rev". No CouchDB os documentos são versionados, isto é, eles não são alterados diretamente, o que acontece é que criamos uma nova revisão com os dados atualizados.
No primeiro contato com esse conceito eu fiquei impressionado pois eu poderia rastrear facilmente todas as modificações em um documento porém não tinha me dado conta que esse conceito vai um pouco além dessa impressão superficial. Esse esquema de revisão substitui um dos grandes problemas dos bancos de dados relacionais, o lock.

No CouchDB não existe um lock tradicional, ele se baseia no conceito de lock otimista. Basicamente só podemos atualizar um documento se a revisão corrente no banco for igual a revisão que buscamos originalmente. Caso não seja o banco retornará um erro que deve ser tratado pelo programador.
Escalabilidade do CouchDB
Um dos grandes problemas dos bancos de dados relacionais é a dificuldade de "escalar" esses bancos. Essa dificuldade não quer dizer que esses bancos são piores que o CouchDB mas sim que eles possuem propósitos diferentes.
Os bancos de dados relacionais surgiram em um momento anterior a internet, onde o número de acessos ao banco era algo muito mais previsível. No mundo pós internet as coisas mudaram, um banco de dados pode receber milhares de acessos por segundo sendo necessário mais de uma instancia para servir aos usuários. É nesse momento os bancos de dados tradicionais começam a se tornar o gargalo e que o CouchDB pode ser a solução.
Como o CouchDB responde através de uma interface de Web Services é extremamente simples colocar um load balancer na frente de um cluster de bancos e distribuir a carga entre eles. Trata-se de um modelo de escalabilidade bastante conhecido e difundido.

Esse esquema resolve o problema de distribuir o acesso a todos os bancos do cluster, porém ainda existe o problema de replicar os dados entre todos os bancos. No CouchDB é extremamente simples fazer replicação de dados, basta uma única chamada HTTP para invoca-la.
curl -vX POST http://127.0.0.1:5984/_replicate -d '{"source":"addresses","target":"http://10.10.10.2:5984/addresses-replica"}'
As Views do CouchDB.
As views são utilizadas para extrair dados dos documentos e são baseadas nos conceitos de MapReduce. A função de MAP extrai os dados dos documentos enquanto a função de REDUCE executa cálculos sobre os dados retornados pela função de MAP. Views podem ser permanentes ou temporárias.
Veja alguns exemplos de views:
Map View

Map View

MapReduce

Conclusão
Se você tem problemas com banco de dados, não gosta de banco de dados ou está cansado de perder tempo estude um pouco sobre CouchDB. Porém realmente acho que você deveria estudar E USAR um pouco antes de rebater dizendo que tudo é fácil com bancos relacionais.
OBS: Todas as imagens utilizadas nesse post foram retiradas do site do CouchDB.
Segunda, 20 de Julho de 2009, 02:08 hs
Parabéns! Faz algum tempo que estudo o couchdb e ansioso em usar!
Segunda, 20 de Julho de 2009, 02:21 hs
Obrigado Fabio.
Assine o RSS pois pretendo escrever mais um pouco sobre CouchDB nos próximos dias.
[]'s
Segunda, 20 de Julho de 2009, 02:36 hs
Parabéns, ótimo post. Estou estudando este cara também mas preciso exercitar mais MapReduce. Você pretende falar mais sobre MapReduce?
[]'s
Segunda, 20 de Julho de 2009, 02:38 hs
Pretendo sim, mas antes vou falar um pouco sobre CouchDB e aplicações Rails.
[]'s
Segunda, 20 de Julho de 2009, 03:25 hs
Então qualquer linguagem que consegue consumir webservices consegue também se comunicar com o CouchDB?
Segunda, 20 de Julho de 2009, 03:28 hs
Weverton, sim, certamente.
E vou mais além, nem precisa consumir webservices... Você pode ir até um nível ainda mais baixo, de usar o curl no SO e criar um parser de JSON.
Enfim, basta lidar com HTTP.
[]'s
Segunda, 20 de Julho de 2009, 10:46 hs
Show, Tapa! Espero ansioso os próximos posts! :)
Minha única preocupação com o CouchDB é se ele seria razoavelmente bom para aplicações com muitas reduções, por exemplo, uma ferramenta OLAP, onde me parece mais aplicável uma banco de dados relacional.
Segunda, 20 de Julho de 2009, 18:50 hs
Muito bom seu post, valeu mesmo pela otima introducao.
Quanto ao ampla capacidade de uso do CouchDB e de bancos acessiveis via REST as vantagens sao enormes, primeiramente eh HTTP o que por si jah eh um avanco, depois temos como tu citou o fato de que qqr coisa pode consumir, alias teus exemplos usando curl foram ohtimos, fantastico pra mostrar o QUAO flexivel isso eh. Basicamente passamos de um "mundo" onde voce precisa de adaptadores para acessar um DB para um ambiente onde voce nao precisa nada mais do que um bom cliente HTTP. Jah vi aplicacoes consumindo REST inteiramente feitas em shell script como tu exemplificou, ou com C (usando libcurl), mas basicamente qualquer coisa eh capaz e mais ainda se tu usa JS, aih a brincadeira fica mais divertida ainda, dado que eh retornado JSON :D
O esquema de revisoes realmente inibe problemas de lock.
Minha principal duvida fica com relacao a validacoes (referenciais por exemplo). Dado que eh um banco sem schema, como se define validacoes? Ou nao se define? Neste caso, ele presume que eu mantenha toda logica na minha aplicacao? Se sim, isso me eva ao problema real: se tenho duas aplicacoes, preciso ou duplicar minhas regras de negocois ou acabar tedo que criar uma middleware que me entregue esses dados, como isso eh trabalhado?
No teu diagrama "Layered Systems" tem no cantinho ali um "validation script", que eh aquilo? Como funciona?
Terça, 21 de Julho de 2009, 14:54 hs
Everton,
As validações ficam fora do banco, na aplicação mesmo. Com relação a forma de implementar e remover as duplicações eu acho que foge um pouco ao escopo do banco, fica mais no campo da programação mesmo.
Mesmo nos bancos tradicionais temos o mesmo problema, se deixamos nossas lógicas nos modelos e temos mais aplicações temos que compartilhar os modelos de alguma forma. Novamente, isso não seria do escopo do banco e sim da programação.
No caso do diagrama(que não é meu hehe) ele coloca as validações como automações, novamente fora do banco.
Terça, 21 de Julho de 2009, 14:56 hs
Fred,
Sinceramente eu não sei dizer. Não tenho experiência com esse tipo de sistema para saber as reais necessidades.
Só fazendo experimentos mesmo. Seria um trabalho interessante se eu tivesse tempo!
[]'s
Terça, 21 de Julho de 2009, 22:59 hs
Parabéns! =)
Quinta, 23 de Julho de 2009, 15:54 hs
Olá Tapajós,
Excelente post, eu ainda desconhecia o funcionamento do CouchDB. Com esta introdução acredito que vou estudar e implementar alguma coisa utilizando este banco de dados.
Obrigado pela contribuição à comunidade. P.S.: Parabéns pela ótima pergunta "Fred" !
Segunda, 27 de Julho de 2009, 11:23 hs
Você chegou a ver se existem ferramentas (backup, otimização, monitoramento) para este SGBD?
Terça, 28 de Julho de 2009, 00:18 hs
Anselmo, vamos por partes.
Backup: Essa parte é bem simples e existem duas abordagens.
1 - Você pode ter um banco replica, usando o esquema de replicação do CouchDB que é bem eficiente.
2 - Você pode fazer dumps do seu banco e restaurar facilmente usando algumas ferramentas em pyton.
Otimização: Realmente não sei se entendi seu questionamento sobre otimização, mas no post eu expliquei que como esse banco responde por requisições HTTP o problema de escalar esse banco é um problema similar a escalar webservers.
Outro detalhe é que uma parte das otimizações está ligado a forma como você vai gerar suas funções de Map & Reduce.
Monitoramento: Se na questão de monitoramento você estiver se referindo a monitorar o status do banco isso é como monitorar servidores web, basta fazer algumas consultas ao servidor. Agora se você estiver se referindo a monitoramento de carga etx eu realmente não sei lhe informar se existe alguma ferramento.
Acho que cabe destacar que o CouchDB é um projeto MUITO recente e com um paradgima diferente, pode ser que ainda surjam mais ferramentas ou pode ser que elas nunca sejam necessárias. Isso só o tempo dirá.
[]'s
Terça, 04 de Agosto de 2009, 16:33 hs
Tapajós, vocês usam CouchRest aí, né?
Acho essa gem bem interessante, inclusive, fiz um pequeno post sobre ela outro dia desses.
Tem alguma limitação nela que você queira comentar? Alguma coisa que foi um entrave em algum desenvolvimento seu, etc.
Terça, 04 de Agosto de 2009, 23:48 hs
Leandro,
Estamos usando o CouchRest sim, trata-se de uma gem muito boa mesmo.
No nosso desenvolvimento comecamos com uma versão meio antiga dela, com isso não tínhamos validação dos dados e tivemos que implementar uns modulos para isso.
Porém a versão mais atual possui um suporte muito bom para callbacks e validações e jogamos nosso código no lixo e estamos bem felizes.
De resto não tenho nada de relevante para comentar sobre a gem.
[]'s
Quarta, 05 de Agosto de 2009, 18:53 hs
Tapajós, só mais uma... hehehe
Vocês chegaram a testar o RelaxDB?
Eu cheguei a ele através dessa apresentação:
http://www.slideshare.net/langalex/ruby-sittin-on-the-couch
E desse repo:
http://github.com/langalex/couchdbexamplewiki
Quarta, 05 de Agosto de 2009, 18:55 hs
Ah! O repo do RelaxDB, pra quem quiser dar uma olhada:
http://github.com/paulcarey/relaxdb
=)
Sábado, 08 de Agosto de 2009, 16:09 hs
Tudo é fácil com bancos relacionais... Brincadeira!!! :-P
Muito bom o artigo. Parabéns! A idéia do CouchDB é muito massa mesmo. Vou começar a estudar e fazer alguns testes aqui. Vlw!
Terça, 11 de Agosto de 2009, 19:15 hs
Muito bom o seu artigo. Gostei mesmo. Simples e bem escrito. Quanto ao couchdb, já vinha namorando-o a tempo e tentando criar uma oportunidade para usá-lo. Por enquanto tem me servido para um idéia de base de conhecimento.
Sexta, 14 de Agosto de 2009, 11:26 hs
Ótimo artigo parabéns tapajós. Já tinha ouvido falar do CouchDB em uma conversa com o Daniel Lopes, e a questão que eu levantei para ele lhe faço também. Hoje já é possível usar o CouchDB em projetos comerciais, projetos de sistemas grandes por exemplo?
Terça, 18 de Agosto de 2009, 16:27 hs
Excelente post... valeu
Quinta, 10 de Setembro de 2009, 04:21 hs
Gostei da idéia do CouchDB, mas não consigo pensar onde usá-lo, além de log... sei que é para Web, mas não vejo onde. Poderia desculpar a ignorância e dar umas sugestões?
valeu
Quinta, 24 de Setembro de 2009, 21:15 hs
Eu cheguei a testar o relexDB por um tempo, funciona bem apesar das constantes mudanças no plugin, uma delas quebrou meu aplicativo de testes e foi foda para achar onde estava o problema, graças a ajuda do Akita agente achou. http://github.com/madrugao/relaxdb/commit/abe62f7a41ef85faf9829d600ac553682390a5f7