MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

População de mangusto vs aninhamento de objetos


A primeira coisa a entender sobre a população de mangustos é que não é mágica, mas apenas um método de conveniência que permite recuperar informações relacionadas sem fazer tudo sozinho.

O conceito é essencialmente para uso onde você decide que precisará colocar os dados em uma coleção separada em vez de incorporar esses dados, e suas principais considerações devem ser normalmente no tamanho do documento ou onde essas informações relacionadas estão sujeitas a atualizações frequentes que tornariam manter os dados incorporados de difícil manuseio.

A parte "não mágica" é que essencialmente o que acontece nos bastidores é que quando você "referencia" outra fonte, a função populate faz uma consulta/consultas adicionais a essa coleção "relacionada" para "mesclar" esses resultados do pai objeto que você recuperou. Você pode fazer isso sozinho, mas o método existe por conveniência para simplificar a tarefa. A consideração óbvia de "desempenho" é que não há uma única viagem de ida e volta ao banco de dados (instância do MongoDB) para recuperar todas as informações. Sempre há mais de um.

Como amostra, faça duas coletas:
{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        ObjectId("5392fee10ff066b7d533a766"),
        ObjectId("5392fefe0ff066b7d533a767")
    ]
}

E os itens:
{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }

O "melhor" que pode ser feito por um modelo "referenciado" ou pelo uso de populate (sob o capô) é este:
var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();

Portanto, há claramente "pelo menos" duas consultas e operações para "juntar" esses dados.

O conceito de incorporação é essencialmente a resposta do MongoDB para como lidar com o não suporte a "junções". Então, em vez de dividir os dados em coleções normalizadas, você tenta incorporar os dados "relacionados" diretamente no documento que os usa. As vantagens aqui são que há uma única operação de "leitura" para recuperar as informações "relacionadas" e também um único ponto de operações de "gravação" para atualizar as entradas "pai" e "filho", embora muitas vezes não seja possível gravar em "muitos" filhos de uma só vez sem processar "listas" no cliente ou aceitar "várias" operações de gravação e, de preferência, em processamento "lote".

Data então se parece com isso (em comparação com o exemplo acima):
{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
        { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
    ]
}

Portanto, na verdade, buscar os dados é apenas uma questão de:
db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });

Os prós e contras de ambos sempre dependerão muito do padrão de uso do seu aplicativo. Mas de relance:

Incorporação


  • O tamanho total do documento com dados incorporados normalmente não excederá 16 MB de armazenamento (o limite BSON) ou caso contrário (como diretriz) terá matrizes que contêm 500 ou mais entradas.

  • Os dados incorporados geralmente não requerem alterações frequentes. Portanto, você pode conviver com a "duplicação" que vem da desnormalização, não resultando na necessidade de atualizar essas "duplicações" com as mesmas informações em muitos documentos pai apenas para invocar uma alteração.

  • Dados relacionados são frequentemente usados ​​em associação com o pai. O que significa que, se seus casos de "leitura/gravação" estiverem sempre precisando "ler/gravar" para pai e filho, faz sentido incorporar os dados para operações atômicas.

Referência


  • Os dados relacionados sempre excederão o limite BSON de 16 MB. Você sempre pode considerar uma abordagem híbrida de "bucketing", mas o limite rígido geral do documento principal não pode ser violado. Casos comuns são "postar" e "comentários" em que se espera que a atividade de "comentários" seja muito grande.

  • Os dados relacionados precisam de atualização regular. Ou essencialmente o caso em que você "normaliza" porque esses dados são "compartilhados" entre muitos pais e os dados "relacionados" são alterados com frequência suficiente para que seja impraticável atualizar itens incorporados em cada "pai" em que esse item "filho" ocorre . O caso mais fácil é apenas referenciar o "filho" e fazer a alteração uma vez.

  • Há uma separação clara de leituras e gravações. No caso de talvez você nem sempre exigir essa informação "relacionada" ao ler o "pai" ou de não precisar alterar sempre o "pai" ao escrever para a criança, pode haver uma boa razão para separar o modelo conforme referenciado. Além disso, se houver um desejo geral de atualizar muitos "subdocumentos" de uma só vez, nos quais esses "subdocumentos" são na verdade referências a outra coleção, muitas vezes a implementação é mais eficiente quando os dados estão em um arquivo separado. coleção.

Portanto, na verdade, há uma discussão muito mais ampla sobre os "prós/contras" para qualquer posição na documentação do MongoDB sobre modelagem de dados, que abrange vários casos de uso e maneiras de abordar o uso de incorporação ou modelo referenciado, conforme suportado pelo método populate.

Esperamos que os "pontos" sejam úteis, mas a recomendação geral é considerar os padrões de uso de dados do seu aplicativo e escolher o que é melhor. Ter a "opção" de incorporar "deveria" ser a razão pela qual você escolheu o MongoDB, mas na verdade será como seu aplicativo "usa os dados" que tomará a decisão de qual método se adequa a qual parte de sua modelagem de dados (já que não é "tudo ou nada") o melhor.
  1. Observe que, como isso foi originalmente escrito, o MongoDB introduziu o $lookup operador que de fato realiza "junções" entre coleções no servidor. Para os propósitos da discussão geral aqui, é "melhor" na maioria das circunstâncias que a sobrecarga de "consulta múltipla" incorrida por populate() e "várias consultas" em geral, ainda há uma "sobrecarga significativa" incorridos com qualquer $lookup operação.

O princípio de design central é "incorporado" significa "já está lá" em oposição a "buscar de outro lugar". Essencialmente, a diferença entre "no seu bolso" e "na prateleira", e em termos de E/S geralmente mais como "na prateleira da biblioteca no centro" , e notavelmente mais distante para solicitações baseadas em rede.