noSQL Brasil

Sábado 15 de maio de 2010 vai rolar em São Paulo o primeiro encontro de noSQL do Brasil.

O noSQL Brasil é o primeiro encontro brasileiro que visa apresentar, promover e discutir as tecnologias “noSQL”. Para isso, serão realizadas palestras sobre as diversas abordagens noSQL com exemplos práticos e demonstrações, bem como um painel onde será discutido como e quando utilizar noSQL.

Com certeza estarei presente e já quero parabenizar os organizadores do evento por essa excelente iniciativa que vai ajudar a fortalecer a comunidade noSQL brasileira.

http://nosqlbr.com/

CouchRest ExtendedDocument – Queries Básicas

Em ruby temos algumas gems para serem utilizadas com o CouchDB. Particularmente eu prefiro a gem CouchRest, construída pelos commiters do CouchDB (Chris Anderson é um deles). E um dos commiters do CouchRest (que vem fazendo um excelente trabalho) é o Marcos Tapajos da Improve IT e RedeParede.
Para saber mais como instalar e utilizar o CouchRest acesse a wiki no Github.

Vamos considerar a seguinte classe (Post) que herda ExtendedDocument da gem CouchRest

class Post < CouchRest::ExtendedDocument
  use_database DB #constante com as infos do seu banco

  property :titulo
  property :conteudo

  #será criado no banco uma view pelo título
  view_by :titulo

end

Carregando todos os documentos com o “couchrest-type” Post

@posts = Post.all
puts @posts.inspect

Carregando um documento pelo ID

#Sem excessões caso o documento não seja encontrado
@post = Post.get("id_do_documento")
puts @post.inspect

#Será gerado uma excessão se o documento não for encontrado
@post = Post.get!("id_do_documento")
puts @post.inspect

Utilizando Views

A view criada pelo CouchRest

function(doc) {
  if ((doc['couchrest-type'] == 'Post') && doc['titulo']) {
    emit(doc['titulo'], null);
  }
}
#Carregando todos do documentos com "couchrest-type" Post e que tenham um título
@posts = Post.by_titulo
puts "O número de posts encontrados foi #{@posts.count}"
puts
puts @posts.inspect

#Carregando todos do documentos com "couchrest-type" Post e um título específico
@posts = Post.by_titulo(:key => "Um título em específico")
puts @posts.inspect

CouchDB 0.11.0

Ontem foi lançada a versão 0.11 do CouchDB que é a base antes da versão 1.0.
Algumas das melhorias foram:

Adicionado suporte a instalador Windows
Melhora nas views
Correção de bugs na construção de views
Melhorias nos cálculos de estatísticas
Melhorias de velocidade e concorrência em pesquisas
Corrigido timeout de HTTP para replicação
Melhorias no Futon UI
Melhorias nos anexos (attachments)
Melhorias na autenticação
Adicionado um manipular de reescrita de url
Adicionado um manipulador de atenticação de proxy
Adicionado habilidade de replicar documentos pelo ID
Adicionado a criação de virtual hosts

São muitas as melhorias que a equipe do CouchDB vem realizando, com certeza o projeto está bem próximo da maturidade para a versão 1.0

Propriedades ACID

Ultimamente escrevi alguns posts sobre o CouchDB, um deles referente as propriedades ACID.
As propriedades ACID (atomicidade, consistência, isolamento e durabilidade) são fundamentais nos bancos de dados, sejam os relacionais ou os orientados a documentos. Então, também é valido tratarmos desse assunto referente aos bancos relacionais, em um contexto geral.
Atualmente os sistemas de informação suportam vários usuários. O banco de dados tem que garantir a confiabilidade nas transações, haja vista que muitas podem ocorrer concorrentemente.

O que é uma transação?

Uma transação é um programa em execução que forma uma unidade lógica de processamento no banco de dados. Uma transação inclui uma ou mais operações de acesso ao banco de dados — englobam operações de inserção, exclusão, alteração ou recuperação. *

Por que a Restauração (Recuperação) é Necessária?

O sistema deverá garantir que: (1) todas as operações na transação foram com­pletadas com sucesso e seu efeito será gravado permanentemente no banco de dados ou (2) a transação não terá nenhum efei­to sobre o banco de dados ou sobre quaisquer outras transações. *

Atomicidade
A propriedade de atomicidade garante que as transações sejam atômicas (indivisíveis). A transação será executada totalmente ou não será executada.

Consistência
A propriedade de consistência garante que o banco de dados passará de uma forma consistente para outra forma consistente.

Isolamento
A propriedade de isolamento garante que a transação não será interferida por nenhuma outra transação concorrente.

Durabilidade
A propriedade de durabilidade garante que o que foi salvo, não será mais perdido.

* Algumas respostas foram retiradas do livro: Sistemas de banco de dados – Ramez Elmasri e Shamkant B. Navathe.

Compactar banco de dados – CouchDB

O CouchDB assim como alguns bancos relacionais (MySQL, Postgre, Oracle etc) utiliza o modelo MVCC (Multiversion concurrency control), então quando um documento recebe uma atualização, é criada uma nova versão do documento.

Devido ao fato da criação de novas versões, seu banco de dados pode começar a crescer exponencialmente. Então você pode compactar o banco, para economizar certo espaço em disco. A compactação comprimi o arquivo de banco de dados removendo seções não utilizadas que foram criadas durante as atualizações.

A compactação pode ser feita enviando uma requisição HTTP Post para o “sub recurso” _compact do seu banco. Em caso de sucesso um status HTTP 202 é retornado.

curl -X POST http://localhost:5984/meu_banco/_compact
#=> {"ok":true}

Você pode verificar informações sobre sua base (inclusive se uma compactação está ocorrendo) enviando uma requisição HTTP Get ao banco.

curl -X GET http://localhost:5984/meu_banco
#=> {"db_name":"meu_banco", "doc_count":334, "doc_del_count":0, "update_seq":5329, "purge_seq":0, "compact_running":false, "disk_size":614498, "instance_start_time":"1267717198892433", "disk_format_version":4}

É recomendado que a compactação seja feita em um horário em que o CouchDB não receba muitas escritas.

Autenticar usuário no CouchDB

Por padrão o CouchDB não exige que seja configurado uma conta com usuário e senha, porém criar uma conta é simples.
Para criar uma conta edite o arquivo local.ini

sudo vim /usr/local/etc/couchdb/local.ini 

Altere require_valid_user para true na sessão couch_httpd_auth

[couch_httpd_auth]
require_valid_user = true

Acrescente o usuário e senha na sessão admins

[admins]
admin = senha_admin

Reinicie o CouchDB

sudo /usr/local/etc/init.d/couchdb restart

O CouchDB irá criar um hash com a senha:

[admins]
admin = -hashed-c3574aef8a2969b53a8c33edf28cb538d997397c,51e3cce80f8e 809804cfcbe481263bf5

Se você estiver programando em ruby e utilizando a gem couchrest, você pode fazer a conexão assim:

CouchRest.database!("http://admin:senha_admin@127.0.0.1:5984/base_de_dados")

CouchDB – Implementação

O CouchDB é feito na plataforma Erlang OTP, uma linguagem de programação funcional, concorrente e uma plataforma de desenvolvimento. Erlang foi desenvolvida para aplicações em tempo real de telecomunicações com ênfase na extrema confiabilidade e disponibilidade.

Na sintaxe e na semântica, Erlang é muito diferente de linguagens de programação convencionais como C ou Java. Erlang usa “processos” leves e passagem de mensagem por concorrência, não tem compartilhamento de estado de thread e todos os dados são imutáveis. A natureza robusta e concorrente do Erlang é ideal para um servidor de banco de dados.

O CouchDB é projetado para concorrência livre de locks, no modelo conceitual e na implementação atual do Erlang. Reduzindo gargalos e evitando locks mantém o sistema inteiro funcionando previsivelmente sobre cargas pesadas. O CouchDB pode acomodar muitas mudanças de replicação de clientes, abrir e atualizar documentos, consultar views cujos índices estão sendo simultaneamente sendo atualizados por outros clientes, sem precisar de locks.

Para alta disponibilidade e mais usuários concorrentes, o Couchdb é projetado para um cluster “shared nothing”. Em um cluster “shared nothing”, cada máquina é independente e replica dados com seus companheiros de cluster, deixando falhas individuas de servidores com zero de downtime. E porque scans de consistência e consertos não são necessários no restart, se o cluster inteiro falhar – devido a uma queda de energia no datacenter, por exemplo – todo o sistema distribuído do CouchDB volta a ficar disponível depois de um restart.

O CouchDB é construído desde o início com uma visão consistente de um sistema distribuído de banco de dados orientado a documento. Ao contrário de tentativas pesadas em construir recursos distribuídos no topo da mesma herança de modelos e base de dados, ele é o resultado de um projeto, engenharia e integração cuidadosos. O documento, view, segurança e modelos de replicação, a proposta especial da linguagem de query, o layout robusto e eficiente de disco e a natureza concorrente e real da plataforma Erlang são cuidadosamente integrados para um sistema real e eficiente.

Essa é uma tradução adaptada:
http://couchdb.apache.org/docs/overview.html

CakePHP vs Ruby on Rails

O CakePHP é um framework para desenvolvimento rápido de aplicações em PHP. Baseado no Ruby on Rails, segue o paradigma da convenção sobre configuração. Usa design patterns como MVC e ORM, reduz os custos de desenvolvimento e ajuda os desenvolvedores a escreverem menos código.

Apesar de ser um bom framework e ser baseado no Rails, a principal diferença que faz o Rails mais vantajoso (fora o nível da comunidade ruby ser maior que o nível da comunidade php) é a própria sintaxe do Ruby em relação ao PHP. Eu particularmente recomendo fortemente o desenvolvimento em PHP utilizando o CakePHP, mas na minha opinião nada supera o Rails.

Abaixo segue um exemplo de uma consulta (inner join) entre dois modelos (Agencia e Cidade que possuem um relacionamento has_and_belongs_to_many) para uma consulta em que devem ser listadas as cidades (distintamente) que possuem relacionamento com agências ordenadas pelo nome:

O SQL esperado

SELECT cidades.id, cidades.nome FROM `cidades` INNER JOIN agencias_cidades ON (`agencias_cidades`.`cidade_id` = `cidades`.`id`) GROUP BY `cidades`.`id` ORDER BY `cidades`.`nome` ASC

Um possível exemplo no CakePHP (no controller agencias)

function index() {

        $cidades = $this->Agencia->Cidade->find('all', array(
            'recursive' => 0,
            'fields' => array('Cidade.id, Cidade.nome'),
            'joins' => array(array(
                'table' => 'agencias_cidades',
                'type' => 'INNER',
                'conditions' => array('agencias_cidades.cidade_id = Cidade.id')
            )),
            'order' => array('Cidade.nome'),
            'group' => array('Cidade.id')
        ));
        
        $this->set('cidades', $cidades);
}

Um possível exemplo no Ruby on Rails (no controller agencias)

def index
	@cidades = Cidade.all :select => "cidades.id, cidades.nome",
        :joins => "INNER JOIN agencias_cidades ON (`agencias_cidades`.`cidade_id` = `cidades`.`id`)",                          
        : order => "cidades.nome",
        :group => "cidades.id"
end

Esse é um exemplo bem simples, porém fica nítido a simplicidade do código, mesmo sem levar em consideração que no Rails bastaria uma linha em cada modelo para definir o relacionamento, enquanto que no Cake seria necessário mais linhas.

Recommend Me

CouchDB Views – exemplo prático

O CouchDB possui uma interface web (melhor que um phpmyadmin por exemplo) para administração dos dados, chamada Futon. Para acessá-la abra em seu browser:

http://localhost:5984/_utils/

Vamos iniciar com um exemplo bem simples de como utilizar views.
As views são usadas para recuperar os dados do banco. De início talvez pareça um pouco complicado, a maioria de nós está completamente adaptada ao SQL, mas aos poucos vamos mudando nossa forma de pensar (até mesmo quebrando a forma “relacional” de armazenamento dos dados) e vamos aprendendo conceitos interessantes, como Map/Reduce.

Vamos supor que precisamos armazenar dados de estudantes (nome e idade apenas). Nos bancos relacionais teríamos que criar uma tabela “estudantes” com os devidos campos e tipos. Isso pode não ser tão bom, já que nem todos os estudantes podem ter as mesmas informações (representação dos dados). Os documentos são bem melhores para representarmos os elementos da vida real, do que as tabelas, são extremamente flexíveis. No CouchDB o documento (JSON) ficaria mais ou menos assim:

Um detalhe para o atributo type (que eu criei) para “informar” qual o tipo de documento, no caso se refere a estudantes.

Vamos supor que teríamos outros documentos no banco (de outros “types” também) e que gostaríamos de selecionar apenas as informações de estudantes. Em SQL seria:

SELECT * FROM students

No CouchDB poderíamos criar uma view (em javascript) assim:

function(doc) {
  if(doc['type'] == 'Student') {
    emit(doc['name'], doc);
  }
}

Detalhe que essa é uma função do tipo Map. Serão selecionados apenas os documentos com type igual a “Student” e a chave é o nome do estudante.

Existe uma série de vantagens em utilizar o conceito de Map/Reduce, principalmente quando se trata de aplicações que rodam em clusters.
Você também pode ler como funcionam os índices de views no Couch.

View Indexes

Antes de começar com a tradução sobre os índices de views do CouchDB, quero fazer um agradecimento especial ao Marcos Tapajós pela divulgação em CouchDB para brasileiros. O Tapajós trabalha para o Redeparede, para a Improve It e é commiter da gem CouchRest. Valeu Tapajós =]

Índices de views

As views são representações dinâmicas do conteúdo atual dos documentos de um banco de dados, e o CouchDB faz ser simples a criação de views úteis dos dados. Mas gerar uma view de um banco de dados com centenas de milhares ou milhões de documentos leva tempo e consome recursos, não é algo que o sistema deve fazer do zero toda hora.

Para manter a exibição de consultas rápida, a view engine mantém índices dessas views, e atualiza-os incrementalmente para refletir as mudanças no banco de dados. O design do core do CouchDB é largamente otimizado para criação eficiente de views e seus índices.

Visões e suas funções são definidas dentro de documentos especiais de “design” e um documento de design pode conter várias funções de views (unicamente nomeadas). Quando um usuário abre uma view e seu índice é automaticamente atualizado, todas as views no mesmo documento de design são indexadas em um único grupo.

O construtor de views usa o ID de sequência do banco de dados para determinar se um grupo de views está totalmente atualizado com o banco de dados. Se não, a view engine examina todos os documentos do banco (embalados em ordem sequencial) mudados desde a última atualização. Os documentos são lidos na ordem em que estão os arquivos no disco, reduzindo a frequência e custo de buscas no disco.

As views podem ser lidas e consultadas simultaneamente enquanto estão também sendo atualizadas. Se um cliente está recebendo o conteúdo de uma view grande, a mesma view pode ser aberta e atualizada concorrentemente por outro cliente sem bloquear o primeiro cliente. Isso é verdade para qualquer número de leituras de clientes simultâneos, que podem ler e consultar a view enquanto o índice está concorrentemente sendo atualizado por outros clientes sem causar problemas para os leitores.

Assim os documentos são examinados, os seus valores de linha anterior são removidos dos índices de views, se existirem. Se o documento for selecionado por uma função de view, os resultados da função são inseridos na view como uma nova linha.

Quando as mudanças nos índices de views são escritas no disco, as atualizações são sempre anexadas ao fim do arquivo, servindo tanto para reduzir as buscas no disco durante commits e para garantir que crashes e falhas de falta de energia não corrompam os índices. Se uma falha ocorrer durante uma atualização de um índice, a atualização incompleta do índice é simplesmente perdida e reconstruída incrementalmente a partir do seu estado anterior.

Essa é uma tradução adaptada:
http://couchdb.apache.org/docs/overview.html