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
Erlang
Imagem via Wikipedia

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


Views no CouchDB

Views são o método de agregação e relatórios sobre os documentos em uma base de dados (os SELECTs dos bancos relacionais), e são construídas por demanda para agregar, juntar e reportar documentos. As views são construídas dinamicamente e não afetam o documento subjacente, você poder ter várias representações diferentes dos mesmos dados que desejar.

As definições de views são estritamente virtuais e somente mostram os documentos da instancia corrente do banco de dados, fazendo-os separados dos dados que eles mostram e compatíveis com replicação. As views do CouchDB são definidas dentro de documentos de design especiais e podem ser replicadas em várias instancias de bancos de dados como documentos regulares, de modo que não apenas os dados são replicados no CouchDB, mas o design da aplicação inteira também é replicado.

Funções Javascript

As views são definidas usando funções javascript que atuam como map em um sistema de map-reduce. Uma view pega um documento do CouchDB como um argumento e então faz o cálculo que precisar fazer para determinar os dados que serão disponibilizados pela view, se houverem. É possível adicionar multiplas linhas para a view com base em um único documento.

Índices de Views

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


CouchDB – Propriedades ACID

O CouchDB possui totais características das propriedades ACID (atomicidade, consistencia, isolamento e durabilidade). Em disco, o CouchDB nunca sobrescreve dados salvos ou estruturas associadas, assegurando que os dados estejam sempre em um estado consistente. Trata-se de um design “crash-only” aonde o servidor CouchDB não passa por um processo de shut down, ele é simplesmente terminado.

Alterações nos documentos (adicionar, editar, deletar) são serializadas, exceto os blobs binários que são escritos concorrentemente. Leituras no banco nunca são bloqueadas (lock) e nunca tem que esperar por escritas ou outras leituras. Qualquer número de clientes podem ler documentos sem serem bloqueados ou interrompidos por atualizações concorrentes, mesmo no mesmo documento. O CouchDB lê operações utilizando o modelo MVCC (Multiversion concurrency control) aonde cada cliente ve uma cópia consistente do banco de dados desde o início até o fim da operação de leitura.

Os documentos são indexados em b-trees pelo seu nome (DocID) e um ID de sequência. Cada atualização para uma instância de banco de dados gera um novo número sequencial. IDs de sequência são usados depois para encontrar as mudanças de forma incremental em uma base de dados. Esses índices b-trees (árvores B) são atualizados simultaneamente quando os documentos são salvos ou deletados. A atualização dos índices sempre ocorre no final do arquivo (append-only updates).

Os documentos têm a vantagem dos dados serem convenientemente “embalados” para armazenamento em vez de dividir em numerosas tabelas e linhas como na maioria dos bancos de dados. Quando os documentos são salvos em disco, os campos dos documentos e metadados são colocados em buffers, sequencialmente um documento depois do outro (depois isso é útil para construção de views).

Quando os documentos do CouchDB são atualizados, todos os dados e índices associados são “descarregados” (flushed) no disco e o commit transacional sempre deixa o banco em um estado completamente consistente. Commits ocorrem em dois passos:

1 – Todos os dados dos documentos e atualizações em índices associados são “esvaziados” (flushed) no disco de maneira síncrona.
2 – O cabeçalho do banco de dados atualizados é escrito em dois pedaços consecutivos e idênticos para compor os primeiros 4k do arquivo, então é “esvaziados” (flushed) no disco de maneira síncrona.

No caso do sistema operacional dar “crash” ou uma falta de energia no passo 1, as atualizações parcialmente liberadas são simplesmente esquecidas ao reiniciar. Se acontecer algum acidente no passo 2 (commitando o cabeçalho), uma cópia sobrevivente idêntica aos cabeçalhos anteriores permanecerão, garantido a coerência de todos os dados previamente salvos. Exceto a área de cabeçalho, checagens de consistencia ou consertos depois de um crash ou falta de energia, nunca são necessários.

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


Como os documentos são armazenados no CouchDB?

O CouchDB armazena os dados em documentos. Cada documento tem um nome único no banco e o CouchDB provê uma API HTTP RESTful para ler e atualizar (adicionar, editar, excluir) os documentos do banco de dados.

Os documentos são as unidades primárias de dados, consistem em um número qualquer de campos e anexos. Os documentos também incluem metadados que são mantidos pelo sistema de banco de dados. Os campos possuem nomes únicos (por documento) e contem valores de vários tipos (texto, número, booleano, listas, etc) e não há qualquer limite definido para o tamanho do texto ou elemento.

O modelo de atualização de documentos do CouchDB é lockless (sem lock) e otimista. Edições nos documentos são feitas por aplicações clientes que carregam os documentos, aplicam as alterações e salvam de volta ao banco de dados. Se outro cliente que estiver editando o mesmo documento salvar as alterações primeiro, o cliente recebe um erro de conflito de edição. Para resolver o conflito de atualização, a versão mais recente do documento pode ser aberta, a edição é reaplicada e a atualização é tentada novamente.

As alterações nos documentos (adicionar, editar, excluir) são “tudo ou nada”, tem sucesso completamente ou falha completamente. O banco nunca contem documentos salvos ou editados parcialmente.

Essa é uma tradução adaptada do início do Overview técnico do CouchDB:
http://couchdb.apache.org/docs/overview.html


MongoDB
MongoDB mugs
Imagem by hoerner_brett via Flickr

No post que eu falo que conheci o conceito de NoSQL e fiz uma pequena tradução sobre o CouchDB eu disse que em breve falaria sobre o MongoDb.

Assim como o Couch o Mongo é um banco de dados orientado a documentos, é open source, também tem como propósito a escalabilidade e é desenvolvido em C++.
Os documentos são armazenados com a simplicidade de esquemas de dados JSON-like, mais precisamente BSON (Binary JSON). Assim como o JSON, suporta a incorporação de objetos e arrays dentro de outros objetos e arrays.
As consultas também são bem simples:

db.clientes.find()

em SQL seria

SELECT * FROM clientes

Suporta Map/Reduce, provê algumas funcionalidades para buscas full text e tem muitas outras vantagens.

Com certeza os bancos orientados a documentos mais falados no momento são o MongoDB e o CouchDB. Não arrisco um papilte em dizer qual é melhor, particularmente eu gosto bastante do CouchDB. Notei que o MongoDB está em uma fase de desenvolvimento mais evoluída que o CouchDB, porém acho o Couch um pouco mais simples, até mesmo de instalar.
Como referência aqui no Brasil, temos o pessoal da Improve It utilizando o Couch e o pessoal do time de Cloud Computing da Locaweb utilizando o Mongo.
E você prefere qual? Por que?