Pontos de dados

Afinal de contas, o que são os bancos de dados de documentos?

Julie Lerman

 

Julie LermanA essa altura, existe uma boa chance de que você tenha, pelo menos, ouvido o termo NoSQL. Artigos foram escritos sobre ele na MSDN Magazine. Muitas pessoas que respeito profundamente estão empolgadas com ele e, tendo eu sido criado com bancos de dados relacionais, gostaria de entender melhor o espaço. Eu pesquisei bastante e incomodei muito os amigos para entender o conceito e aqui vou compartilhar o que aprendi sobre um subconjunto dos bancos de dados NoSQL chamado “banco de dados de documentos”. Outro subconjunto são os bancos de dados par chave-valor. O armazenamento de tabela do Windows Azure, sobre o qual eu escrevi na minha coluna sobre pontos de dados em julho de 2010 (msdn.microsoft.com/magazine/ff796231), é um exemplo de armazenamento de par chave-valor do NoSQL.

Antes, eu deveria abordar a definição de NoSQL. Ele se tornou um termo bastante comum e, possivelmente, usado de maneira exagerada. O termo é usado para abranger mecanismos de armazenamento não tradicionais e que, portanto, não exigem o uso do SQL para acesso a seus dados. Em sua postagem de blog, “Abordando as críticas ao NoSQL” (bit.ly/rkphh0), o especialista em CouchDB e autor Bradley Holt diz ter ouvido pessoas “redefinirem o NoSQL como ‘não somente SQL’”. Seu ponto é que isso não se trata, de maneira alguma, de um movimento anti-SQL. Eu gosto dessa perspectiva, pois sou forte adepto do uso da ferramenta correta para a tarefa.

A maioria dos bancos de dados classificados como não relacionais compartilham objetivos comuns de velocidade e escalabilidade. Ao se afastarem do modelo tradicional de armazenamento e abandonarem os esquemas, esses bancos de dados estão livres das limitações a eles impostas por um esquema rigidamente vinculado e à necessidade de seus aplicativos unirem dados entre tabelas.

Dos muitos bancos de dados de documentos disponíveis, vou me concentrar em dois dos mais populares: o MongoDB (mongodb.org) e o CouchDB (couchdb.apache.org), assim como o RavenDB (ravendb.net), que foi escrito para o Microsoft .NET Framework e cresce em popularidade (consulte o artigo “Incorporando o RavenDB a um aplicativo ASP.NET MVC 3”, nesta adição). Este artigo será superficial, embora você possa obter mais detalhes sobre os bancos de dados individuais e o que os torna diferentes uns dos outros, visitando seus sites.

Exceto por algumas poucas variações (que apresentarei neste artigo), esses bancos de dados fornecem seus dados geralmente por HTTP, armazenam seus dados como documentos em JavaScript Object Notation (JSON) e fornecem APIs em várias linguagens. As preocupações gerais são simplicidade, velocidade e escalabilidade. Igualmente importante é que todos os três são projetos de código-fonte aberto.

Durante minha pesquisa, ouvi um especialista em MongoDB afirmar ser o desempenho a preocupação principal do produto. Um especialista em CouchDB indicou a simplicidade e confiabilidade (“queremos ser o Honda Accord dos bancos de dados”). E Ayende Rahien, criador do RavenDB, disse que o RavenDB objetiva “gravações rápidas, leituras rápidas e paz no mundo”.

Uma alternativa e não um substituto aos bancos de dados relacionais.

O NoSQL e bancos de dados de documentos fornecem uma alternativa a bancos de dados relacionais, não uma substituição. Cada um ocupa seu lugar e eles simplesmente lhe oferecem mais opções passíveis de escolha. Mas como escolher? Uma avaliação importante é o teorema de consistência, disponibilidade e tolerância ao particionamento (CAP). Ele diz que ao trabalhar com sistemas distribuídos, você pode contar somente com duas de três garantias (C, A ou P), de modo que você deve escolher as que forem importantes. Se consistência for importante, então você deve ficar com um banco de dados relacional.

Um exemplo comum de onde a consistência seria a garantia mais importante é em aplicativos bancários ou, quem sabe, um que opere uma instalação nuclear. Nesses cenários, é importante que cada parte dos dados seja contabilizada a cada instante. Se alguém faz uma retirada, você precisa realmente ser informado sobre ela ao consultar o saldo de sua conta. Portanto, você provavelmente vai querer um banco de dados relacional com um alto nível de controle sobre suas transações. Um termo que você ouvirá bastante é “consistência eventual” ou, conforme citado no site do RavenDB: “melhor obsoleto do que offline”. Em outros domínios, a consistência eventual é suficiente. Não há problema se os dados que você estiver recuperando não forem exatos a cada milissegundo.

Talvez, então, seja mais importante que alguma versão dos dados esteja disponível, em vez de se esperar até que todas as transações sejam atualizadas. Isso diz respeito ao A (availability = disponibilidade) no CAP, que se concentra no tempo de disponibilidade do servidor. Saber que você terá sempre acesso ao banco de dados tem precedência e consiste em um enorme benefício ao desempenho do banco de dados (isto é, bancos de dados de documentos são rápidos!). Você verá que o P, tolerância a particionamento, também é importante especialmente na escalabilidade horizontal.

API HTTP RESTful — na maioria das vezes

Muitos dos bancos de dados NoSQL são acessíveis no modo RESTful, de modo que você pode conectar a seu banco de dados através de um URI e as consultas e comandos são chamadas HTTP. O MongoDB é uma exceção. Por padrão, ele usa TCP para interações de bancos de dados, embora haja também pelo menos uma API HTTP disponível. O CouchDB e o MongoDB fornecem APIs específicas de idioma que o permitem gravar e executar consultas e atualizações sem ter que se preocupar com a criação direta de chamadas HTTP. O RavenDB tem uma API cliente .NET que simplifica a interação com o banco de dados.

Dados relacionados em um único registro

Muitos presumem incorretamente que bancos de dados não relacionais são arquivos simples. Os documentos armazenados em um banco de dados de documentos são capazes de conter dados formatados: árvores com nós. Cada registro no banco de dados é um documento e pode ser um conjunto autônomo de dados. Ele é autodescritivo, inclusive seu esquema possivelmente exclusivo, e não é necessariamente dependente de nenhum outro documento.

Segue um exemplo típico de como um registro se apresenta em um banco de dados de documento (vou roubar uma amostra de um tutorial do MongoDB que representa um aluno):

{
  "name" : "Jim",
  "scores" : [ 75, 99, 87.2 ]
}

E aqui vai um do artigo de introdução do CouchDB, que descreve um livro:

{
  "Subject": "I like Plankton"  
  "Author": "Rusty"  
  "PostedDate": "5/23/2006"  
  "Tags": ["plankton", "baseball", "decisions"]
  "Body": "I decided today that I don't like baseball. I like plankton."
}

Essas são estruturas simples com dados de sequência de caracteres, números e matrizes. Você pode também incorporar objetos a objetos para uma estrutura de documento mais complexa, como esse exemplo de postagem de blog: 

{
  "BlogPostTitle”: “LINQ Queries and RavenDB”,
  "Date":"\/Date(1266953391687+0200)\/",
  "Content":”Querying RavenDB is very familiar for .NET developers who are already
    using LINQ for other purposes”,
  "Comments":[
             {
             "CommentorName":"Julie",
             "Date":"\/Date(1266952919510+0200)\/",
             "Text":"Thanks for using something I already know how to
               work with!",
             "UserId":"users/203907"             
             },
  ]
}

Chaves exclusivas

Todos os bancos de dados necessitam de uma chave. Se você não fornecer uma, eles criarão uma para você, internamente. Chaves são importantes para a capacidade de indexação dos bancos de dados, mas seu próprio domínio pode exigir que você tenha chaves conhecidas. No exemplo anterior de postagem de blog, note que há uma referência a “users/203907”. É assim que o RavenDB utiliza valores de chaves e permite que você defina os relacionamentos entre documentos.

Armazenamento no formato JSON

O que todos esses exemplos de registros têm em comum é que eles usam o JSON para armazenar seus dados. O CouchDB, o RavenDB e muitos outros, de fato armazenam seus dados em JSON. O MongoDB usa um componente em JSON chamado Binary JSON (BSON), capaz de executar serialização binária. O BSON é a representação interna dos dados, de modo que, do ponto de vista de programação, você não deve notar nenhuma diferença.

A simplicidade de JSON torna fácil a transposição de estruturas de objeto de quase qualquer linguagem para JSON. Portanto, você pode definir seus objetos em seu aplicativo e armazená-los diretamente no banco de dados. Isso libera os desenvolvedores da necessidade de usar um mapeador relacional de objetos (ORM) para constantemente traduzir entre o esquema do banco de dados e o esquema classe/objeto.

Mecanismos de pesquisa de texto completo, tais como o Lucene (lucene.apache.org), no qual se baseia o RAvenDB, fornecem pesquisa de alto desempenho nesses dados baseados em texto.

Observe a data neste exemplo de postagem de blog. O JSON não tem um tipo de data, mas cada um dos bancos de dados fornece um modo para interpretar tipos de data a partir de qualquer que seja a linguagem na qual você está codificando. Se você verificar a lista de tipos de dados e convenções para a API BSON do MongoDB (bit.ly/o87Gnx), você verá que um tipo de data é acrescentado, em conjunto com alguns outros, para esclarecer o que se encontra disponível no JSON.

Armazenar e recuperar dados em uma única unidade pode trazer enormes benefícios de desempenho e escalabilidade. Os bancos de dados não precisam sair procurando por dados que sejam relacionados entre si, pois eles estão todos juntos.

Coleções de tipos

Ao interagir com o banco de dados, como que seu aplicativo sabe que um item é um aluno, o outro um livro e ainda outro uma postagem de blog? Os bancos de dados usam um conceito de coleções. Qualquer documento, independente de seu esquema, que esteja associado a uma determinada coleção — por exemplo, uma coleção de alunos — pode ser recuperado quando solicitar dados daquela coleção. Também não é incomum usar um campo para indicar o tipo. Isso só torna as pesquisas muito mais fáceis, mas cabe ao seu aplicativo impor o que deve e o que não deve pertencer a uma coleção.

Banco de dados sem esquema

O “aluno” descrito anteriormente contém seu próprio esquema. Cada registro é responsável por seu próprio esquema, inclusive aqueles contidos em um único banco de dados ou em uma única coleção. E o registro de um aluno não necessariamente corresponde ao registro de outro aluno. É claro que seu software terá que acomodar quaisquer diferenças. Você poderia simplesmente usar essa flexibilidade para obter eficiência. Por exemplo, por que armazenar valores nulos? Você poderia fazer o seguinte quando uma propriedade como “most_repeated class” não tiver valor:

"name" : "Jim",
"scores" : [ 75, 99, 87.2 ]
"name" : "Julie",
"scores" : [ 50, 40, 65 ],
"most_repeated_class" : "Time Management 101"

Sim, Virgínia, nós damos suporte a transações

Cada um dos bancos de dados fornece algum nível de suporte a transações — alguns mais que outros — mas nenhum é tão avançado quanto o que se pode conseguir com um banco de dados relacional. Vou desviar para sua documentação e deixá-lo acompanhar com sua própria pesquisa adicional.

Bancos de dados de documentos e desenvolvimento controlado por domínio

Um dos principais conceitos do desenvolvimento controlado por domínio refere-se à modelagem de seu domínio usando raízes agregadas. Ao planejar suas classes de domínio (as quais podem se tornar documentos em seu banco de dados), você pode procurar por dados que sejam, na maioria das vezes, autônomos (por exemplo, um pedido com seus itens de linha) e considerá-los como uma estrutura de dados individual. Em um sistema de pedidos, você provavelmente terá também clientes e produtos. Mas um pedido pode ser acessado sem precisar da informação sobre o cliente e um produto pode ser usado sem precisar de acesso aos pedidos em que ele é usado. Isso significa que embora você encontre muitas oportunidades de ter estruturas de dados autônomas (tais como um pedido com seus itens de linha), isso não exclui a necessidade ou a funcionalidade de unir dados através de chaves estrangeiras em certos cenários.

Cada um dos bancos de dados fornece orientação em vários padrões que se encontram disponíveis e com os quais seus usuários estão tendo mais sucesso. Por exemplo, a documentação do MongoDB discorre sobre um padrão chamado matriz ancestral, que acelera o acesso a dados relacionados ao unir documentos.

Preocupações com relações de navegação limitam-se ao fato de que em um banco de dados relacional a repetição de dados é um pecado. Bancos de dados são normalizados para garantir isso. Ao trabalhar com bancos de dados NoSQL, especialmente aqueles que são distribuídos, a desnormalização de seus dados é útil e aceitável.

Consultando e atualizando

Cada banco de dados vem com APIs para consulta e atualização. Embora elas possam não fazer parte da API principal, diversas APIs de linguagem são fornecidas através de complementos. Como uma entrada do .NET Framework no mundo de bancos de dados, o RavenDB usa o LINQ para consultas — uma bela vantagem para os desenvolvedores de .NET.

Outras consultas dependem de visualizações predefinidas e de um padrão chamado mapeamento/redução. A parte de mapeamento desse processo usa as visualizações e a responsabilidade do mapa difere entre bancos de dados. O mapa também permite que o banco de dados distribua o processamento de consultas por vários processadores. A redução usa o resultado da consulta por mapeamento (ou consultas, se ela foi distribuída) e agrega os dados aos resultados a serem retornados ao cliente.

Mapeamento/redução é um padrão e os vários bancos de dados têm suas próprias implementações. Rob Ashton oferece uma comparação interessante de como o RavenDB e o CouchDB desempenham o mapeamento/redução em bit.ly/94OCME.

Enquanto o CouchDB exige que você consulte através de uma visualização mapeamento/redução predefinida, o MongoDB (também usando visualizações e mapeamento/redução) fornece também a capacidade de se fazer consultas ad hoc. O RavenDB permite índices predefinidos para consulta, mas também dá suporte a consultas ad hoc e criará índices automaticamente para você, com base em suas consultas em tempo de execução real. Na maioria das vezes, entretanto, ao se afastar de esquemas conhecidos e da natureza relacional dos bancos de dados SQL, a capacidade de executar consultas ad hoc é um dos recursos que você perde. Mantendo um controle rígido sobre as consultas, os bancos de dados de documentos são capazes de prometer seu rápido desempenho.

Uma revolução de banco de dados

tantos bancos de dados relacionais por aí, sob o guarda-chuva do NoSQL. E agora que a porta se abriu, ela está inspirando a vinda de mais outros, à medida que as pessoas observam o que está disponível e sonham em como poderão aprimorar isso. Eu acho que o RavenDB é um bom exemplo disso, e você pode observar como Rahien está evoluindo o banco de dados enquanto continua a sonhar sobre como torná-lo melhor ou é inspirado pelos usuários.

Eu acredito que o mistério envolvendo esses bancos de dados seja contagioso. Eu definitivamente anseio por pesquisar em mais detalhes e aprender mais. Mas mesmo os três que eu olhei são tão interessantes que fica difícil para esse libriano escolher entre eles, porque no momento eu estou resolvendo um problema de curiosidade e não um verdadeiro problema de negócios e os bancos de dados relacionais vêm a se encaixar perfeitamente em meus projetos atuais.

Julie Lerman é uma MVP da Microsoft, mentora e consultora do .NET, que reside nas colinas de Vermont. Você pode encontrá-la fazendo apresentações sobre acesso a dados e outros tópicos do Microsoft .NET em grupos de usuários e conferências em todo o mundo. Seu blog está em thedatafarm.com/blog e ela é autora do livro altamente reconhecido, “Programming Entity Framework” (O’Reilly Media, 2010). Siga-a no Twitter, em twitter.com/julielerman.

Agradecemos aos seguintes especialistas técnicos pela revisão deste artigo: *Ted Neward e *Savas Parastatidis