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

Desempenho de consulta do MongoDB para mais de 5 milhões de registros


Isso é procurar a agulha no palheiro. Precisaríamos de alguma saída de explain() para aquelas consultas que não funcionam bem. Infelizmente, mesmo isso resolveria o problema apenas para essa consulta específica, então aqui está uma estratégia sobre como abordar isso:
  1. Certifique-se de que não seja por causa de RAM insuficiente e paginação excessiva
  2. Ative o criador de perfil de banco de dados (usando db.setProfilingLevel(1, timeout) onde tempo limite é o limite para o número de milissegundos que a consulta ou comando leva, qualquer coisa mais lenta será registrada)
  3. Inspecione as consultas lentas em db.system.profile e execute as consultas manualmente usando explain()
  4. Tente identificar as operações lentas no explain() saída, como scanAndOrder ou nscanned grande , etc.
  5. Motivo sobre a seletividade da consulta e se é possível melhorar a consulta usando um índice de qualquer forma . Caso contrário, considere desabilitar a configuração de filtro para o usuário final ou dê a ele uma caixa de diálogo de aviso de que a operação pode estar lenta.

Um problema importante é que você aparentemente está permitindo que seus usuários combinem filtros à vontade. Sem a interseção de índice, isso aumentará drasticamente o número de índices necessários.

Além disso, lançar cegamente um índice em todas as consultas possíveis é uma estratégia muito ruim. É importante estruturar as consultas e garantir que os campos indexados tenham seletividade suficiente .

Digamos que você tenha uma consulta para todos os usuários com status "ativo" e alguns outros critérios. Mas dos 5 milhões de usuários, 3 milhões estão ativos e 2 milhões não, então acima de 5 milhões de entradas há apenas dois valores diferentes. Esse índice geralmente não ajuda. É melhor pesquisar os outros critérios primeiro e depois verificar os resultados. Em média, ao devolver 100 documentos, você terá que digitalizar 167 documentos, o que não prejudicará muito o desempenho. Mas não é tão simples. Se o critério principal for o joined_at data do usuário e a probabilidade de usuários descontinuarem o uso com o tempo é alta, você pode acabar tendo que escanear milhares de documentos antes de encontrar uma centena de correspondências.

Portanto, a otimização depende muito dos dados (não apenas de sua estrutura , mas também os dados em si ), suas correlações internas e seus padrões de consulta .

As coisas pioram quando os dados são muito grandes para a RAM, porque ter um índice é ótimo, mas a varredura (ou mesmo simplesmente retornar) os resultados podem exigir buscar muitos dados do disco aleatoriamente, o que leva muito tempo.

A melhor maneira de controlar isso é limitar o número de tipos de consulta diferentes, proibir consultas em informações de baixa seletividade e tentar impedir o acesso aleatório a dados antigos.

Se tudo mais falhar e se você realmente precisar de muita flexibilidade nos filtros, pode valer a pena considerar um banco de dados de pesquisa separado que suporte interseções de índice, buscar os IDs do mongo de lá e obter os resultados do mongo usando $in . Mas isso está repleto de seus próprios perigos.

-- EDITAR --

A explicação que você postou é um belo exemplo do problema com a varredura de campos de baixa seletividade. Aparentemente, há muitos documentos para "[email protected]". Agora, encontrar esses documentos e classificá-los em ordem decrescente por carimbo de data/hora é muito rápido, porque é suportado por índices de alta seletividade. Infelizmente, como existem apenas dois tipos de dispositivos, o mongo precisa escanear 30.060 documentos para encontrar o primeiro que corresponda a 'móvel'.

Suponho que isso seja algum tipo de rastreamento da web, e o padrão de uso do usuário torna a consulta lenta (se ele alternasse entre celular e web diariamente, a consulta seria rápida).

Tornar essa consulta específica mais rápida pode ser feita usando um índice composto que contém o tipo de dispositivo, por exemplo, usando
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})

ou
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})

Infelizmente, isso significa que consultas como find({"username" :"foo"}).sort({"timestamp" :-1}); não pode mais usar o mesmo índice, então, conforme descrito, o número de índices crescerá muito rapidamente.

Receio que não haja uma solução muito boa para isso usando o mongodb no momento.