"Como devo acessar meus dados?", é a pergunta frequente dos desenvolvedores quando pensam em uma solução de armazenamento. Para responder a essa pergunta, primeiro é preciso entender o aplicativo que está sendo considerado. Quem são os usuários mais importantes e quais casos de uso precisam ser rápidos, ou seja, quais ações o usuário realiza com frequência? Qual é o hot path?
Depois de entender o hot path, você estará pronto para examinar o armazenamento.
Certas ações, como o upload de um arquivo grande, não são tão sensíveis à latência quanto outras. Por exemplo, clicar em "comprar agora" em um site de comércio eletrônico ou visualizar sua linha do tempo nas mídias sociais se beneficia muito mais dos recursos de armazenamento e recuperação de alto desempenho.
Portanto, para cada aplicativo, se você adotar uma perspectiva de desempenho na arquitetura, verá que há lugares em que o usuário passa muito tempo, onde a capacidade de resposta é fundamental. Não se trata apenas de ser rápido, mas de ser rápido de forma confiável sob carga. Os noves são importantes, especialmente para a latência.
Você quer que seus casos de uso mais importantes e cruciais sejam rápidos. Como a memória é muito mais rápida do que o disco, esses casos de uso devem ser atendidos a partir da memória. Não é por acaso que os bancos de dados baseados em memória estão em ascensão. Especialmente em ambientes de nuvem, o fornecimento a partir da memória isola o aplicativo contra o desempenho inconsistente do disco.
Priorize seu esquema em torno do hot path
Mesmo com um banco de dados sem esquema, seu aplicativo imporá um esquema. Cabe a você fazer com que esse esquema favoreça os casos de uso de alto desempenho. Portanto, identificar os tipos de objetos mais importantes e mantê-los em um bucket totalmente residente é uma etapa. Portanto, se você tiver 4 GB de dados de carrinho de compras em tempo real, reserve mais de 4 GB de RAM somente para isso. Se você tiver 20 GB de dados de histórico de compras, talvez não haja problema em dedicar apenas 5 GB de RAM a esses tipos de documentos. Isso permite que os efeitos de disco maior que a memória entrem em ação para os dados menos sensíveis à latência, preservando o desempenho dos dados mais importantes em todas as circunstâncias.
Agora que você está pensando em termos de hot path, vamos falar sobre visualizações. Elas são muito flexíveis (a chave aqui é a capacidade de projetar os dados em tempo real em formas mais vantajosas para consulta). Como você vai querer que seu hot path seja executado em um modelo de valor-chave, você acabará fazendo muitos ponteiros e multi get. Ou seja, um aplicativo de alto desempenho vinculará firmemente seu modelo de dados ao hot path. Isso pode tornar o suporte aos casos de uso menos quentes um pouco mais desafiador. É aqui que as visualizações entram em cena. As visualizações são poderosas o suficiente para dar uma nova vida a essas estruturas de dados orientadas pelo tempo de execução como índices passíveis de consulta. O resultado é que você obtém um modelo de consulta flexível além do desempenho do valor-chave.
Para dar um exemplo específico, veja o conjunto de dados de amostra de cervejas. Talvez em seu aplicativo, a tela principal que os usuários queiram ver seja uma lista de cervejas com suas classificações e, para cada cerveja, a capacidade de carregar rapidamente as avaliações da cerveja escritas pelos usuários finais. Nesse caso, você desejará fazer pesquisas de valores-chave em massa (em vez de consultas de exibição) para preencher essas telas com o melhor desempenho possível. Se um caso de uso secundário for pesquisar cervejarias por cidade, esse é um bom momento para usar exibições, pois elas podem fornecer uma janela flexível para os dados subjacentes.
Vamos começar examinando os próprios documentos. Neste caso, estamos armazenando classificações diretamente nos documentos de cerveja. Ao incorporar as classificações, podemos mostrá-las trivialmente na interface do usuário, sem consultas ou pesquisas adicionais. Também estamos vinculando diretamente as avaliações ("comentários") do documento da cerveja, de modo que uma cerveja e todos os seus comentários possam ser obtidos sem a necessidade de uma consulta de visualização baseada em disco. Essa técnica garante que, mesmo sob uma enorme carga de tráfego, os tempos de resposta do banco de dados serão rápidos.
Aqui está uma ilustração:
O documento azul é um documento de perfil de usuário e é referenciado em alguns outros lugares no esquema. Sempre que já tivermos o user_id, poderemos procurar o perfil do usuário rapidamente. Portanto, procure esse ID nos outros documentos JSON.
O documento amarelo é um comentário/resenha sobre uma cerveja. Ele tem a identificação da cerveja, mas as pesquisas de um comentário para a cerveja em questão são raras. O mais comum é que você tenha a cerveja em mãos e queira procurar as resenhas. Mencionamos anteriormente que, em nosso exemplo hipotético, consideramos que essa é uma seção de desempenho crítico, portanto, embora seja possível usar exibições para pesquisar avaliações por beer_id, nesse caso, queremos fazer nossas pesquisas por meio da interface de valor-chave. Ao trabalhar nessa interface, obtemos o benefício das velocidades na memória, bem como mais escalabilidade, pois essas solicitações usam menos recursos do servidor.
O documento verde é um documento real sobre cerveja. Temos as classificações em linha, armazenadas sob o user_id para impor a restrição de que cada usuário só pode classificar cada cerveja uma vez. Para as avaliações/comentários, criamos um link para eles a partir da matriz de comentários. Portanto, o código para buscar todos os dados necessários para exibir uma página para uma cerveja é mais ou menos assim (o exemplo não está em uma linguagem de programação específica).
reviews = couchbase.multiget(beer.comments);
profiles = couchbase.multiget(reviews.map{|review|review.user_id});
Em seguida, a página terá dados suficientes para exibir informações sobre todas as avaliações da cerveja, bem como informações sobre o usuário que deixou a avaliação. Tudo isso exigiu apenas três solicitações ao banco de dados, de modo que o tempo total decorrido deve ser de apenas alguns milissegundos, o que será mais rápido do que o estilo alternativo de enviar uma consulta complexa ao banco de dados, para que ele possa construir um conjunto de resultados e retorná-lo.
Exibições em camadas sobre o esquema de alto desempenho
Há muita riqueza na estrutura do documento que projetamos para facilitar as interações de valor-chave. Embora não estejamos mantendo um caminho de pesquisa de valor-chave para descobrir as cervejas mais bem classificadas, como as classificações estão incorporadas diretamente no documento, é fácil escrever uma exibição que classifique as cervejas por classificação. A consulta a essa exibição não será tão rápida quanto uma pesquisa direta de valor-chave, mas algo como as cervejas mais bem avaliadas pode ser facilmente armazenado em cache, para que os usuários tenham uma experiência de alto desempenho, mesmo que o índice subjacente seja baseado em disco e não na memória.
Aqui está uma visualização de mapa que classificaria as cervejas por classificação média.
Se (doc.ratings) {
var total = 0, count = 0;
for (var user_id in doc.ratings) {
total = total + doc.ratings[user_id]; count++
}
emit(total/count, null);
}
}
Você pode consultar isso com ?descending=true para obter primeiro as cervejas mais bem avaliadas.
A partir desse mesmo conjunto de dados subjacente, também podemos fornecer uma maneira de um determinado usuário encontrar todas as avaliações que ele deixou. Essa não é uma operação comum (em nosso aplicativo de exemplo inventado), portanto, não é importante que seja extremamente rápida. Portanto, teria sido uma grande dor de cabeça manter um caminho de pesquisa de valor-chave para isso. Por exemplo, se os seus usuários raramente tentam encontrar todas as avaliações que escreveram, é muito difícil manter uma lista de IDs de avaliação anexada a cada perfil de usuário. Em vez disso, basta marcar as avaliações com o ID do usuário e usar um índice para apoiar a consulta. A exibição para encontrar todas as avaliações do usuário X é simples:
Se (doc.type == "comment" && doc.user_id) {
emit(doc.user_id, null);
}
}
Você consulta isso com ?key=525 para encontrar todas as avaliações escritas pelo usuário número 525.
Conclusão: Projete para o hot-path, deixe a flexibilidade do NoSQL ajudar com o resto
Esperamos que este artigo tenha apresentado um quadro suficientemente claro sobre como arquitetar seu aplicativo para as seções críticas de desempenho. A melhor coisa que você pode fazer é personalizar seu esquema para que as páginas que precisam ser mais rápidas sejam as mais fáceis de carregar do banco de dados. É claro que isso significa que não será tão rápido carregar páginas não críticas, pois o esquema não foi projetado especificamente para torná-las rápidas.
No entanto, as exibições do Couchbase facilitam o reaproveitamento dos dados para seus outros padrões de acesso. Esperamos que os exemplos acima mostrem como você pode fazer essencialmente o mesmo tipo de consulta de duas maneiras muito diferentes, dependendo de suas restrições de desempenho.
Bônus: Sobre exibições em cache
Se o seu hot path incluir uma consulta de visualização (por exemplo, em uma linha do tempo inicial de uma rede social), você deverá armazená-la em cache. Isso significa que, embora a primeira solicitação possa levar alguns milissegundos, as solicitações posteriores terão um alto desempenho consistente. Isso é mais típico do uso do memcached com o mysql. Nesse padrão, o memcached é usado tanto para acelerar o desempenho percebido quanto para manter a carga fora do banco de dados. Estamos falando essencialmente da mesma coisa aqui, exceto que, em vez de colocar os resultados de uma consulta lenta do mysql no memcached, colocamos os resultados de uma consulta de exibição do Couchbase em um bucket de memória.
O TTL do Couchbase pode lidar com a expiração do cache para você. Ou é possível usar alguma validação de cache mais avançada
estratégias (uma história para outro dia...)
[...] estratégias de modelagem, mas isso está fora do escopo deste artigo. Para saber mais sobre isso, leia "Arquitetura orientada ao desempenho", de Chris [...]