Desenvolvimento com Amazon DynamoDB

A Amazon fornece um kit para desenvolvimento (software development kit ou SDK em inglês) para as tecnologias: Android, iOS, Java, .NET, PHP e Ruby. Esse kit permite o desenvolvimento e integração com toda a plataforma de serviços em nuvem da Amazon, inclusive com o DynamoDB.

Como o Kwitter foi desenvolvido em .NET, existem duas formas de realizar a codificação:

  • Primeira é utilizando .NET Low-Level API que fornece um controle maior sobre a codificação (pode-se realizar a analogia com o ADO.NET).
  • Segunda é .NET Object Persistence Model que possui uma abstração ainda maior agilizando o desenvolvimento de rotinas mais simples (análogo ao ADO.NET Entity Framework).

Nesse projeto utilizou-se uma junção de ambos. Na parte de gravação de registros, foi utilizado .NET Object Persistence Model, já, na parte de consultas seletivas (query), foi utilizada a .NET Low-Level API. Isso permitiu um controle melhor e mais especifico buscando o melhor desempenho nas consultas.

O principal motivo pelo qual o DynamoDB foi escolhido entre os bancos de dados apresentados foi pelo fornecimento da infraestrutura juntamente com o SGBD. Como já mencionado, esse critério foi plenamente atendido pela Amazon, fornecendo uma arquitetura robusta e escalável a um valor justo.

Uma das principais ressalvas é com relação ao modelo de consistência eventual. Porém, isso não se tornou um problema em vista que a mensagem é um registro estável, uma vez gravada não é mais alterada, o que pode ocorrer é a alteração na taxonomia necessitando uma reanalise de cada mensagem, mas isso não é uma necessidade constante.

Mesmo que as mensagens fossem seguidamente alteradas, seria necessária uma quantidade muito grande de acessos multiusuário ao mesmo registro para causar problemas, já que a latência da aplicação é muito baixa.

Uma das grandes dificuldades percebida é que a equipe de desenvolvimento tem que se adaptar aos novos tempos. O desenvolvimento tem que pensar em como fazer melhor uso da arquitetura, pois agora o custo de uso é medido rapidamente. No modelo de banco de dados relacional quando uma aplicação não está obtendo muito desempenho deixa-se ao cargo do DBA criar índices ou stored procedure. Neste paradigma a criação de uma nova tabela vai necessitar de desenvolvimento, além de um monitoramento das métricas do sistema, mas enquanto isso não acontece o custo de aumentar a vazão de uma determinada tabela pode ser alto.

O DynamoDB atendeu todas as expectativas de um banco de dados, o autor desse trabalho com base na experiência de 8 anos utilizando o modelo relacional, o DynamoDB se encaixaria em muitas dessas aplicações, em alguns casos muito melhor que o banco relacional adotado. Um exemplo é o sistema para controle de ponto eletrônico onde é necessário armazenar cada uma das batidas de cada funcionário. Continuar lendo

Uma agulha num palheiro

Encontrar um dado específico num mundo de Big Data pode ser mais difícil do que encontrar uma agulha em um palheiro.

Amazon DynamoDB

O DynamoDB é um banco de dados NoSQL totalmente gerenciado que fornece rapidez e performance. Ele automaticamente distribui os dados e o tráfego das tabelas a um número de servidores que seja capaz de lidar com as requisições dos clientes. Os dados são armazenados em discos Solid State Disks (SSD) e são automaticamente replicados através de múltiplas regiões para prover alta disponibilidade e durabilidade.

O DynamoDB possui as seguintes características:

  •  Provisionamento de transferência: Durante a criação ou edição da tabela, a capacidade de requisição desejada  pode ser especificada. O SGBD se encarrega de alocar os recursos necessários para garantir a capacidade especificada. O provisionamento é feito através da quantidade de unidades de 1KB, que se deseja gravar ou ler.
  • Escalabilidade de armazenamento: Não existe um limite de quantidade de dados armazenados. O serviço automaticamente aloca mais espaço quando necessário.
  • Distribuição Total: O escalonamento horizontal e automatizado replica a mesma tabela sobre uma centena de servidores.
  • Construído à prova de falhas: O processo automático e assíncrono de replicação de dados através de múltiplos servidores espalhados em diferentes regiões garante além de alta disponibilidade também protege os dados contra falhas individuais ou coletivas de hardware.
  • Consistência forte e contadores atômicos: Diferente de outros BD não relacionais, o DynamoDB facilita o desenvolvimento para garantir a consistência forte durante a leitura, retornando sempre o último valor do registro. Além disso, a API fornece chamadas para incremento e decremento de contadores de forma atômica.
  • Flexibilidade: O BD não possui um esquema fixo de dados, o que significa que cada registro de uma tabela pode ter um número diferente  de atributos e de tipos de dados.

Operações no Banco de Dados

A API fornece as operações de criação, atualização e exclusão de tabelas. A atualização da tabela permite o aumento ou diminuição do provisionamento de transferência. Cada tabela é formada por uma chave primária (campo Hash) e pode ou não ter uma variação (campo Range). O conteúdo de um atributo pode ser: número, literal, conjunto de números ou conjunto de literais.  O tamanho em bytes de cada registro é definido pelo somatório do tamanho dos nomes dos campos mais 0 tamanho binário dos dados.

Também são fornecidos métodos para adicionar, atualizar e excluir registros das tabelas. Durante a atualização de itens é possível modificar valores e adicionar ou remover colunas. Para otimizar as buscas, pode ser utilizada uma operação de retorno de um ou múltiplos items através de sua chave primária, inclusive em múltiplas tabelas.

Leitura e consistência

O SGBD mantém múltiplas cópias de cada item para garantir durabilidade, para que isso aconteça após uma operação de alteração de dados é necessário que o dado seja gravado em múltiplos servidores, o que demora algum tempo. Essa demora faz com que o dado fique temporariamente inconsistente, ou seja, caso uma leitura seja feita imediatamente o valor antigo pode ser retornado. Essa é a forma padrão de leitura. Mas em alguns casos, é necessário utilizar uma leitura consistente, para isso o DynamoDB retorna o mais recente que reflita todas as operações de escrita. Essa forma de leitura é mais suscetível a lentidão da rede.

Controle de Concorrência

Em um ambiente multiusuário, em alguns casos, é necessário garantir que a atualização de um usuário não afete a gravação de outro. Para isso, o DynamoDB suporta a escrita condicional, que nada mais é do que a verificação de valor gravado antes de realizar a gravação de um novo valor.  Para contadores de valor, é possível utilizar operações atômicas que incrementam ou decrementam valores sem serem interferidas por outras operações de gravação.

A figura abaixo apresenta uma simulação de como os dados são solicitados e gravados.

Consultas

Para consultas, existem dois mecanismos: Query e Scan. A Query permite a consulta na tabela utilizando o campo Hash e, opcionalmente, um filtro de Range. Esse mecanismo é o mais eficiente de buscar items na tabela.
A operação de Scan é a forma menos eficiente, pois realiza uma varredura em todos os dados da tabela. Nessa operação é possível realizar pesquisa por valores que não são chave, porém isso implica em uma busca comparativa registro-a-registro.
Para uma melhor performance o Scan somente deve ser utilizado quando a Query não for possível. Para diminuir o tempo de responta do Scan e Query, o retorno das requisições são páginas com tamanho máximo de 1MB e a quantidade de registros é delimitada através de parâmetros.

Continuar lendo

Isso não é um banco de dados relacional

A ordem dos fatores altera o produto

Como já comentei, estou fazendo o TCC baseado em um banco de dados não relacional, neste caso o DynamoDB.

Nestas duas últimas semanas venho desenvolvendo os aplicativos para gravar e ler no DynamoDB (Marvin e Telescreen, respectivamente).

Para gravar, o processo é extremamente simples; basta montar o registro e mandar salvar no dado. Mas tive alguns problemas na hora de retornar o dado. Primeiramente, com o fato de não haver relacionamento entre tabelas. Imaginando as tabelas abaixo, onde cada mensagem pertence a um usuário.

Durante a apresentação, para cada mensagem é necessário fazer uma nova requisição ao servidor, solicitando o Usuário através de seu username. Então para carregar 200 mensagens são necessárias 201 requisições (1 para pegar a lista de mensagens e mais 200 para cada usuário). Todo esse processo se mostrou muito lento, quase 30 segundos para carregar 50 mensagens.

A primeira tentativa para otimizar esse processo foi criando um buffer de usuários, onde ao invés de realizar diretamente a requisição de usuário, era verificado se o registro já estava armazenado na memória local. Este processo não trouxe efeito por causa do tipo de dado que está sendo armazenado. Como são Tweets capturados de acordo com uma taxonomia, a chance de um usuário mandar 2 mensagens próximas é pequena, sendo assim o buffer não se mostrou útil e foi retirado.

A solução para esse problema foi colocar os campos da tabela de usuários junto à tabela de mensagens, não sendo mais necessária a tabela de usuários. Isso produziu uma desnormalização na tabela de usuários, mas o ganho de velocidade em consultas foi interessante. Para retornar 50 mensagens, é necessário 1 segundo.

O outro problema que tive foi com relação à ordem de retorno dos registros. Como o banco de dados realiza sharding, ou seja, os dados são replicados e particionados entre diversos servidores, ao solicitar diversos registros, eles não retorna em uma ordem que eu tenha identificado, nem na ordem de inclusão. É outro problema pois não consigo apresentar as mensagens mais recentes primeiro. A solução para isso foi apresentar cada página de 50 registros ordenada. Mas não sei se é a melhor solução.

Até o presente momento estou gravando e lendo corretamente todos os dados, o retorno das consultas está rápido e tudo está bem.

A difícil e reconfortante arte de aprender

It is a very sad thing that nowadays is so litle useless information

– Oscar Wilde

Como já comentei estou fazendo o meu TCC sobre armazenamento de dados em banco de dados NoSQL.

O banco que escolhi utilizar foi o DyamoDB. Ele é um banco proprietário da Amazon e é da categoria chave-valor, ou seja  é uma grande tabela identificada por uma chave única. É um paradigma não muito diferente dos utilizados nos dicionários em memória, a grande diferença é a escalabilidade suportando milhões de registros.

Ontem comecei a estudar como armazenar os dados no Dynamo, e uma boa fonte de partida foi o material da própria Amazon. Escolhi desenvolver o programa na linguagem .NET (pois tenho mais facilidade)  e existe um SDK muito bom para o Visual Studio.

Demorei algum tempo para entender os exemplos disponíveis e realizar algumas alterações básicas. Mas a lógica é bem simples.

  • Após concluir o cadastro na Amazon,
  • Vá no console da ferramenta
  • E crie uma tabela
  • Informe o nome
  • Informe o tipo de chave que será utilizada, Hash que é uma chave simples ou Hash and Range que é uma chave composta de dois campos
  • Pronto a sua tabela está pronta.
  • As operações disponíveis para as tabelas são: Load (para carregar uma chave), Save (para salvar uma chave), Delete (para excluir uma chave), Scan (para listar todas as chaves) e Query (para consultar)

Como as tabelas não possuem uma estrutura rígida, cada registro pode ter campos diferentes o que facilita bastante a estrutura. Não existe relacionamento entre diferentes tabelas, isso é realizado diretamente durante a programação.

Ao final da minha secção de trabalho eu tinha as tabelas criadas, conforme “DER” abaixo

E realizei a codificação o Marvin gravar nas tabelas Usuário e Mensagem.

Durante os testes de execução do programa, com uma taxonomia limitada, encontrei uma postagem de @lespider publicando uma comparação entre diversos bancos NoSQL (acesse aqui).

O Marvin começou a criar forma, espero que ele não seja muito deprimido.