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

Seguidores - design de banco de dados mongodb


Concordo com a noção geral de outras respostas de que isso é um limite problema relacional.

A chave para os modelos de dados do MongoDB é o peso da gravação, mas isso pode ser complicado para este caso de uso, principalmente por causa da contabilidade que seria necessária se você quisesse vincular usuários a itens diretamente (uma alteração em um grupo que é seguida por muitos dos usuários incorreria em um grande número de gravações e você precisa de algum trabalhador para fazer isso).

Vamos investigar se o modelo de leitura pesada é inaplicável aqui ou se estamos fazendo uma otimização prematura.

A abordagem de leitura pesada


Sua principal preocupação é o seguinte caso de uso:

um problema real de desempenho pode ser quando eu quero pegar todos os grupos que um usuário está seguindo para um item específico [...] porque então eu tenho que encontrar todos os grupos que o usuário está seguindo, e a partir disso encontrar todos o item_groups com o group_id $in e o código do item.

Vamos dissecar isso:

  • Obter todos os grupos que o usuário está seguindo

    Essa é uma consulta simples:db.followers.find({userId : userId}) . Vamos precisar de um índice em userId o que tornará o tempo de execução desta operação O(log n), ou extremamente rápido mesmo para n grande.

  • a partir disso, encontre todos os item_groups com o group_id $in e o código do item

    Agora esta é a parte mais complicada. Vamos supor por um momento que é improvável que os itens façam parte de um grande número de grupos. Em seguida, um índice composto { itemId, groupId } funcionaria melhor, porque podemos reduzir drasticamente o conjunto de candidatos através do primeiro critério - se um item é compartilhado em apenas 800 grupos e o usuário está seguindo 220 grupos, o mongodb só precisa encontrar a interseção deles, o que é comparativamente fácil porque ambos conjuntos são pequenos.

Precisamos ir mais fundo do que isso, no entanto:

A estrutura de seus dados é provavelmente a de uma rede complexa . Redes complexas vêm em muitos sabores, mas faz sentido assumir que seu gráfico de seguidores é quase livre de escala, o que também é praticamente o pior caso. Em uma rede livre de escala, um número muito pequeno de nós (celebridades, super bowl, Wikipedia) atrai muita 'atenção' (ou seja, tem muitas conexões), enquanto um número muito maior de nós tem problemas para obter a mesma quantidade de atenção combinado .

Os pequenos nós não são motivo de preocupação , as consultas acima, incluindo idas e voltas ao banco de dados, estão no intervalo de 2 ms na minha máquina de desenvolvimento em um conjunto de dados com dezenas de milhões de conexões e> 5 GB de dados. Agora, esse conjunto de dados não é enorme, mas não importa qual tecnologia você escolha, será vinculado à RAM porque os índices devem estar na RAM em qualquer caso (localidade de dados e separabilidade em redes geralmente são ruins), e o tamanho da interseção definida é pequeno por definição. Em outras palavras:esse regime é dominado por gargalos de hardware.

E os supernós no entanto?

Como isso seria adivinhação e estou muito interessado em modelos de rede, tomei a liberdade de implementar uma ferramenta de rede drasticamente simplificada com base em seu modelo de dados para fazer algumas medições. (Desculpe, está em C#, mas gerar redes bem estruturadas é difícil o suficiente na linguagem em que sou mais fluente...).

Ao consultar os supernós, obtenho resultados na faixa de 7ms no máximo (que são 12 milhões de entradas em um banco de dados de 1,3 GB, com o maior grupo com 133.000 itens e um usuário que segue 143 grupos.)

A suposição neste código é que o número de grupos seguidos por um usuário não é enorme, mas isso parece razoável aqui. Se não for, eu iria para a abordagem de gravação pesada.

Sinta-se à vontade para brincar com o código. Infelizmente, será necessário um pouco de otimização se você quiser tentar isso com mais de alguns GB de dados, porque simplesmente não é otimizado e faz alguns cálculos muito ineficientes aqui e ali (especialmente o embaralhamento aleatório ponderado beta pode ser melhorado ).

Em outras palavras:eu não me preocuparia com o desempenho da abordagem de leitura pesada ainda . O problema geralmente não é tanto que o número de usuários cresce, mas que os usuários usam o sistema de maneiras inesperadas.

A abordagem da escrita pesada


A abordagem alternativa é provavelmente inverter a ordem de ligação:
UserItemLinker
{
 userId,
 itemId,
 groupIds[]  // for faster retrieval of the linker. It's unlikely that this grows large
}

Este é provavelmente o modelo de dados mais escalável, mas eu não o escolheria a menos que estivéssemos falando de ENORMES quantidades de dados em que o sharding é um requisito fundamental. A principal diferença aqui é que agora podemos compartimentalizar os dados com eficiência usando o userId como parte da chave de fragmentação. Isso ajuda a paralelizar consultas, fragmentar com eficiência e melhorar a localidade de dados em cenários de vários datacenters.

Isso poderia ser testado com uma versão mais elaborada do testbed, mas ainda não encontrei tempo e, francamente, acho que é um exagero para a maioria dos aplicativos.