Melhorar o desempenho de armazenamento e leitura gratuitamente: esquemas planos versus esquemas estruturados
Avalie esse Artigo
Quando desenvolvedores ou administradores que antes eram apenas "seguidores da palavra de modelagem de dados relacionais" começam a usar o MongoDB, é comum ver documentos com esquemas simples. Esse comportamento acontece porque a modelagem de dados relacionais faz você pensar em dados e esquemas em uma estrutura plana e bidimensional chamada tabelas.
No MongoDB, os dados são armazenados como documentos BSON, quase uma representação binária de documentos JSON, com pequenas diferenças. Por isso, podemos criar esquemas com mais dimensões/níveis. Mais detalhes sobre a implementação do BSON podem ser encontrados em sua especificação. Você também pode aprender mais sobre suas diferenças com JSON.
Os documentos do MongoDB são compostos por um ou mais pares chave/valor, onde o valor de um campo pode ser qualquer um dos tipos de dados BSON, incluindo outros documentos, matrizes ou matrizes de documentos.
Usar documentos, arrays ou arrays de documentos como valores para campos permite a criação de um esquema estruturado, onde um campo pode representar um grupo de informações relacionadas. Este esquema estruturado é uma alternativa a um esquema simples.
Vejamos um exemplo de como escrever o mesmo documento
user
usando os dois esquemas:Os dois documentos acima contêm os mesmos dados. O da esquerda,
flatUser
, usa um esquema plano em que todos os pares de campo e valor estão no mesmo nível. O da direita, structuredUser
, emprega um esquema estruturado em que o campo e os valores têm níveis aninhados de acordo com as informações relacionadas dentro do documento.Então, quais são as vantagens de usar um estruturado em vez de um plano? A resposta rápida para quem tem pressa é que um esquema estruturado pode exigir menos armazenamento e ser mais rápido de percorrer do que um esquema simples. Para quem quer saber por quê, precisamos de uma melhor compreensão do BSON.
Para os fins deste artigo, um documento BSON pode ser visto como uma lista de itens, onde cada item representa um par de campo e valor do documento. Um item é composto dos campos
type
, name
, length
e data
do campo em um formato serializado. O campo type
tem um byte de comprimento e indica o tipo de dados no campo data
. O campo name
é o nome do campo em um formulário de string. O campo length
tem quatro bytes e indica o comprimento do campo data
para aqueles types
onde o tamanho não é fixo. O campodata
são os dados reais do par campo-e-valor. Colocando esta definição em uma representação gráfica, temos:Vamos ver como um esquema estruturado usa menos armazenamento do que um esquema simples analisando o par de campo e valor relacionado ao nome do usuário.
No
flatUser
, temos a seguinte tabela de uma perspectiva de armazenamento:Campo e valor | Tipo | Nome do campo | Comprimento do campo | Dados de campo | Total |
---|---|---|---|---|---|
name_first: "john" | 1 byte | 10 bytes | 4 bytes | 4 bytes | 19 bytes |
name_last: "smith" | 1 byte | 9 bytes | 4 bytes | 5 bytes | 19 bytes |
name_middle: "oliver" | 1 byte | 11 bytes | 4 bytes | 6 bytes | 22 bytes |
Somando os tamanhos totais da tabela, o documento simples usa 60 bytes para armazenar o campo e o valor relacionados ao nome do usuário.
Para analisar o armazenamento do
structuredUser
, vamos dividi-lo em duas tabelas. Na primeira tabela, teremos o armazenamento utilizado pelo documento do campo name
e, na segunda tabela, teremos o armazenamento utilizado pelo campo e valor name
.Vamos construir a primeira tabela para o valor/conteúdo do campo
name
:Campo e valor | Tipo | Nome do campo | Comprimento do campo | Dados de campo | Tamanho total |
---|---|---|---|---|---|
first: "john" | 1 byte | 5 bytes | 4 bytes | 4 bytes | 14 bytes |
Último: "Smith" | 1 byte | 4 bytes | 4 bytes | 5 bytes | 14 bytes |
meio: "oliver" | 1 byte | 6 bytes | 4 bytes | 6 bytes | 17 bytes |
Adicionando os tamanhos totais da tabela anterior, o valor/dados de campo do campo
name
usa 45 bytes. Construindo a segunda tabela para o campo e valor name
, obtemos:Campo e valor | Tipo | Nome do campo | Comprimento do campo | Dados de campo | Tamanho total |
---|---|---|---|---|---|
name: { … } | 1 byte | 4 bytes | 4 bytes | 45 bytes | 54 bytes |
O documento estruturado usa 54 bytes para armazenar os valores relacionados ao nome do usuário.
Comparando as tabelas, vemos que a principal diferença é o tamanho de armazenamento do "Nome do campo". O esquema plano utiliza 30 bytes para armazenar os nomes de seus campos, enquanto o esquema estruturado utiliza 19 bytes para armazenar os nomes de seus campos. Isso se deve à repetição da substring "name_" no "Nome do campo" do esquema plano.
Armazenando os dois documentos em uma instância do MongoDB, obteremos um tamanho de 403 bytes para o esquema plano e 307 bytes para o esquema estruturado. Nada mal obter uma melhoria de 24% no armazenamento apenas alterando o esquema, e um documento estruturado é mais fácil de ler e mais agradável de se ver.
Agora, vamos ver como um esquema estruturado é mais rápido de percorrer do que um esquema simples, obtendo o CEP do endereço comercial.
No documento
flatUser
, para chegar ao campo address_work_zip
começando no início do documento, um cursor precisaria realizar uma comparação de nomes de campos12 até chegar ao campo desejado.No documento
structuredUser
, para chegar ao campo address.work.zip
começando no início do documento, um cursor precisaria executar uma comparação de nomes de campo8 . O menor número de comparações aqui se deve ao fato de alguns valores de um par de campo e valor serem um documento. Quando o cursor verifica o campo name
, ele pode pular três campos/comparação — first
, middle
e last
— porque sabe que address.work.zip
não estará dentro de name.<field>
. Quando o cursor verifica o campo address.home
, ele também pode pular cinco campos/comparação — street
, number
, zip
, state
e country
.Para quantificar o ganho de desempenho ao percorrer um esquema estruturado em vez de um esquema plano no MongoDB, foi usado um teste com a seguinte metodologia:
- Para isolar o resultado a ser afetado apenas pela navegação dos documentos, a instância do MongoDB usada foi configurada com armazenamento na memória.
- Documentos com campos 10, 25, 50 e 100 foram utilizados para o esquema simples.
- Documentos com 2x5, 5x5, 10x5 e 20x5 campos foram utilizados para o esquema estruturado, onde 2x5 significa dois campos do tipo documento com cinco campos para cada documento.
- Para forçar o mecanismo do MongoDB a percorrer todos os documentos e todos os campos dentro de cada documento, todas as queries foram feitas em busca de um campo e valor que não estava presente nos documentos.
- Cada consulta foi executada 100 vezes seguidas para cada tamanho e esquema de documento.
- Nenhuma operação simultânea foi executada durante cada teste.
Agora, para os resultados do teste:
Documentos | Plana | estruturado, estruturado | Diferença | Melhoria |
---|---|---|---|---|
10 / 2x5 | 487 ms | 376 ms | 111 ms | 29,5% |
25 / 5x5 | 624 ms | 434 ms | 190 ms | 43,8% |
50 / 10x5 | 915 ms | 617 ms | 298 ms | 48,3% |
100 / 20x5 | 1384 ms | 891 ms | 493 ms | 55,4% |
Como previa nossa teoria, percorrer um documento estruturado é mais rápido do que atravessar um documento plano. Os ganhos apresentados neste teste não devem ser considerados para todos os casos ao comparar esquemas estruturados e simples; as melhorias na navegação dependerão de como os campos e documentos aninhados estão organizados.
Este artigo mostra como usar melhor o MongoDB deployment alterando o esquema do seu documento para os mesmos dados/informações. Outra opção para extrair mais desempenho do sistema do MongoDB é aplicar os padrões de esquema comuns do MongoDB. Nesse caso, você analisará quais dados deve colocar em seu documento/esquema. O artigo Construindo com padrões tem os padrões mais comuns e ajudará significativamente.