Em meu blog anterior, Como usar a modelagem de dados do MongoDB para melhorar as operações de throughput, discutimos as duas principais abordagens de relacionamento de modelagem de dados, ou seja, incorporação e referência. A escalabilidade do MongoDB é bastante dependente de sua arquitetura e, para ser mais específico, de modelagem de dados. Ao projetar um DBM NoSQL, o principal ponto a ser considerado é garantir documentos sem esquema, além de um pequeno número de coleções para facilitar a manutenção. Boa integridade dos dados, adotando a validação de dados por meio de algumas regras definidas antes que o armazenamento seja incentivado. A arquitetura e o design do banco de dados devem ser normalizados e decompostos em várias pequenas coleções como forma de evitar a repetição de dados, melhorar a integridade dos dados e facilitar os padrões de recuperação. Com isso, você pode melhorar a consistência dos dados, atomicidade, durabilidade e integridade do seu banco de dados.
A modelagem de dados não é um empreendimento secundário em uma fase de desenvolvimento de aplicativos, mas uma consideração inicial, pois muitas facetas de aplicativos são realmente realizadas durante o estágio de modelagem de dados. Neste artigo vamos discutir quais fatores precisam ser considerados durante a modelagem de dados e ver como eles afetam o desempenho de um banco de dados em geral.
Muitas vezes você precisará implantar um cluster de seu banco de dados como uma forma de aumentar a disponibilidade de dados. Com um modelo de dados bem projetado, você pode distribuir atividades para um cluster fragmentado de forma mais eficaz, reduzindo assim as operações de taxa de transferência destinadas a uma única instância mongod. Os principais fatores a serem considerados na modelagem de dados incluem:
- Escalabilidade
- Atomicidade
- Desempenho e uso de dados
- Fragmentação
- Indexação
- Otimização de armazenamento
- Estrutura e crescimento do documento
- Ciclo de vida dos dados
1. Escalabilidade
Este é um aumento na carga de trabalho de um aplicativo impulsionado pelo aumento do tráfego. Muitas aplicações têm sempre uma expectativa no aumento do número de seus usuários. Quando há tantos usuários sendo atendidos por uma única instância de banco de dados, o desempenho nem sempre atende às expectativas. Como um gerenciador de banco de dados, você tem o mandato de projetar esse DBM de forma que as coleções e as entidades de dados sejam modeladas com base nas demandas atuais e futuras do aplicativo. A estrutura do banco de dados geralmente deve ser apresentável para facilitar o processo de replicação e fragmentação. Quando você tem mais estilhaços, as operações de gravação são distribuídas entre esses estilhaços de modo que, para qualquer atualização de dados, ela seja feita dentro do estilhaço que contém esses dados, em vez de procurar em um único conjunto grande de dados para fazer uma atualização.
2. Atomicidade
Isso se refere ao sucesso ou falha de uma operação como uma única unidade. Por exemplo, você pode ter uma operação de leitura que envolve uma operação de classificação após buscar o resultado. Se a operação de classificação não for tratada adequadamente, toda a operação não prosseguirá para o próximo estágio.
As transações atômicas são séries de operações que não são divisíveis nem redutíveis, portanto, ocorrem como entidades únicas ou falham como operações únicas. As versões do MongoDB anteriores à 4.0 suportam operações de gravação como processos atômicos em um único nível de documento. Com a versão 4.0, agora é possível implementar transações multidocumento. Um modelo de dados que aprimora as operações atômicas tende a ter um ótimo desempenho em termos de latência. A latência é simplesmente a duração na qual uma solicitação de operação é enviada e quando uma resposta é retornada do banco de dados. Para ser seco, é fácil atualizar dados que estão embutidos em um único documento ao invés de um que é referenciado.
Vamos, por exemplo, considerar o conjunto de dados abaixo
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12,
settings : {
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
}
Se quisermos atualizar a idade aumentando-a em 1 e alterar o local para Londres, podemos fazer:
db.getCollection(‘students’).update({childId: 535523},{$set:{'settings.location':'London'}, $inc:{age:1}}).
Se, por exemplo, a operação $set falhar, automaticamente a operação $inc não será implementada e, em geral, toda a operação falhará.
Por outro lado, vamos considerar os dados referenciados como que existem 2 coleções uma para aluno e outra para configurações.
Coleção de alunos
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12
}
Coleção de configurações
{
childId : "535523",
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
Nesse caso, você pode atualizar os valores de idade e localização com operações de gravação separadas.
db.getCollection(‘students’).update({childId: 535523},{$inc:{age:1}})
db.getCollection('settings’).update({childId: 535523 } , {$set: { 'settings.location':'London'}})
Se uma das operações falhar, isso não afeta necessariamente a outra, pois são realizadas como entidades diferentes.
Transações para vários documentos
Com o MongoDB versão 4.0, agora você pode realizar várias transações de documentos para conjuntos de réplicas. Isso melhora o desempenho, pois as operações são emitidas para várias coleções, bancos de dados e documentos para processamento rápido. Quando uma transação é confirmada, os dados são salvos, enquanto se algo der errado e uma transação falhar, as alterações feitas são descartadas e a transação geralmente será abortada. Não haverá atualização para os conjuntos de réplicas durante a transação, pois a operação só é visível externamente quando a transação é totalmente confirmada.
Por mais que você possa atualizar vários documentos em várias transações, ele vem com um revés de desempenho reduzido em comparação com gravações de documentos únicos. Além disso, essa abordagem é suportada apenas pelo mecanismo de armazenamento WiredTiger, portanto, é uma desvantagem para os mecanismos de armazenamento In-Memory e MMAPv1.
3. Desempenho e uso de dados
Os aplicativos são projetados de forma diferente para atender a diferentes propósitos. Existem alguns que servem para os dados atuais apenas como aplicativos de notícias meteorológicas. Dependendo da estrutura de um aplicativo, deve-se ser capaz de projetar um banco de dados ideal correspondente para atender ao caso de uso necessário. Por exemplo, se alguém desenvolve um aplicativo que busca os dados mais recentes do banco de dados, usar uma coleção limitada será a melhor opção. Uma coleção limitada aprimora a operação de alta taxa de transferência, assim como um buffer, de modo que, quando o espaço alocado é explorado, os documentos mais antigos são sobrescritos e os documentos podem ser buscados na ordem em que foram inseridos. Considerando a recuperação da ordem de inserção, não haverá necessidade de usar indexação e a ausência de uma sobrecarga de índice também melhorará a taxa de transferência de gravação. Com uma coleção limitada, os dados associados são bastante pequenos, pois podem ser mantidos na RAM por algum tempo. Os dados temporais, neste caso, são armazenados no cache, que é bastante lido do que escrito, tornando a operação de leitura bastante rápida. No entanto, a coleção limitada vem com algumas desvantagens, como você não pode excluir um documento a menos que elimine toda a coleção, qualquer alteração no tamanho de um documento falhará na operação e, por último, não é possível fragmentar uma coleção limitada.
Diferentes facetas são integradas na modelagem de dados de um banco de dados dependendo das necessidades de uso. Como visto, os aplicativos de relatório tendem a ser mais intensivos em leitura, portanto, o design deve ser de forma a melhorar a taxa de transferência de leitura.
4. Fragmentação
O desempenho por meio do dimensionamento horizontal pode ser aprimorado por fragmentação, pois as cargas de trabalho de leitura e gravação são distribuídas entre os membros do cluster. A implantação de um cluster de shards tende a particionar o banco de dados em várias pequenas coleções com documentos distribuídos dependendo de alguma chave de shard. Você deve selecionar uma chave de fragmentação apropriada que possa impedir o isolamento da consulta, além de aumentar a capacidade de gravação. Uma melhor seleção geralmente envolve um campo que está presente em todos os documentos da coleção alvo. Com o sharding, há maior armazenamento, pois à medida que os dados crescem, mais shards são estabelecidos para manter um subconjunto desse cluster.
5. Indexação
A indexação é uma das melhores abordagens para melhorar a carga de trabalho de gravação, especialmente quando os campos ocorrem em todos os documentos. Ao fazer a indexação, deve-se considerar que cada índice exigirá 8 KB de espaço de dados. Além disso, quando o índice estiver ativo, ele consumirá algum espaço em disco e memória, portanto, deve ser rastreado para planejamento de capacidade.
Vários noves Torne-se um DBA do MongoDB - Trazendo o MongoDB para a produçãoSaiba mais sobre o que você precisa saber para implantar, monitorar, gerenciar e dimensionar o MongoDBBaixe gratuitamente
6. Otimização de armazenamento
Muitos documentos pequenos em uma coleção tendem a ocupar mais espaço do que quando você tem alguns documentos com documentos sub-incorporados. Ao modelar, deve-se, portanto, agrupar os dados relacionados antes do armazenamento. Com alguns documentos, uma operação de banco de dados pode ser executada com poucas consultas, portanto, acesso aleatório ao disco reduzido e haverá menos entradas de chave associadas no índice correspondente. As considerações neste caso, portanto, serão:use a incorporação para ter menos documentos, o que, por sua vez, reduz a sobrecarga por documento. Use nomes de campo mais curtos se menos campos estiverem envolvidos em uma coleção para não tornar a sobrecarga do documento significativa. Nomes de campo mais curtos reduzem a expressividade, ou seja,
{ Lname : "Briston", score : 5.9 }
economizará 9 bytes por documento em vez de usar
{ last_name : "Briston", high_score: 5.9 }
Use o campo _id explicitamente. Por padrão, os clientes MongoDB adicionam um campo _id a cada documento atribuindo um ObjectId exclusivo de 12 bytes para esse campo. Além disso, o campo _id será indexado. Se os documentos forem muito pequenos, esse cenário será responsável por uma quantidade significativa de espaço no número geral de documentos. Para otimização de armazenamento, você pode especificar o valor do campo _id explicitamente ao inserir documentos em uma coleção. No entanto, certifique-se de que o valor seja identificado de forma exclusiva, pois serve como chave primária para documentos na coleção.
7. Estrutura e crescimento do documento
Isso acontece como resultado da operação de envio em que os subdocumentos são inseridos em um campo de matriz ou quando novos campos são adicionados a um documento existente. O crescimento do documento tem alguns contratempos, ou seja, para uma coleção limitada, se o tamanho for alterado, a operação falhará automaticamente. Para um mecanismo de armazenamento MMAPv1, as versões anteriores a 3.0 realocarão o documento no disco se o tamanho do documento for excedido. No entanto, nas versões posteriores a partir da 3.0, existe um conceito de Power of 2 Sized Allocations que reduz as chances de tais realocações e permite a reutilização efetiva do espaço de registro liberado. Se você espera que seus dados cresçam, convém refatorar seu modelo de dados para usar referências entre dados em documentos distintos em vez de usar um modelo de dados desnormalizado. Para evitar o crescimento de documentos, você também pode considerar o uso de uma estratégia de pré-alocação.
8. Ciclo de vida dos dados
Para um aplicativo que usa apenas os documentos inseridos recentemente, considere usar uma coleção limitada cujos recursos foram discutidos acima.
Você também pode definir o recurso Time to Live para sua coleção. Isso é bastante aplicável para tokens de acesso no recurso de redefinição de senha para aplicativos.
Tempo de Vida (TTL)
Esta é uma configuração de coleção que possibilita ao mongod remover dados automaticamente após uma duração especificada. Por padrão, esse conceito é aplicado a dados de eventos gerados por máquina, logs e informações de sessão que precisam persistir por um período limitado de tempo.
Exemplo:
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )
Criamos um índice createdAt e especificamos algum valor expireAfterSeconds de 3600, que é 1 hora após o momento da criação. Agora, se inserirmos um documento como:
db.log_events.insert( {
"createdAt": new Date(),
"logEvent": 2,
"logMessage": "This message was recorded."
} )
Este documento será excluído após 1 hora a partir do momento da inserção.
Você também pode definir uma hora específica do relógio quando deseja que o documento seja excluído. Para fazer isso, primeiro crie um índice, ou seja:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
Agora podemos inserir um documento e especificar a hora em que ele deve ser excluído.
db.log_events.insert( {
"expireAt": new Date(December 12, 2018 18:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
Este documento será excluído automaticamente quando o valor expireAt for mais antigo que o número de segundos especificado em expireAfterSeconds, ou seja, 0 neste caso.
Conclusão
A modelagem de dados é um empreendimento amplo para qualquer projeto de aplicativo, a fim de melhorar o desempenho do banco de dados. Antes de inserir dados em seu banco de dados, considere as necessidades do aplicativo e quais são os melhores padrões de modelo de dados que você deve implementar. Além disso, facetas importantes das aplicações não podem ser realizadas até a implementação de um modelo de dados adequado.