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:- Certifique-se de que não seja por causa de RAM insuficiente e paginação excessiva
- Ative o criador de perfil de banco de dados (usando
db.setProfilingLevel(1, timeout)
ondetempo limite
é o limite para o número de milissegundos que a consulta ou comando leva, qualquer coisa mais lenta será registrada) - Inspecione as consultas lentas em
db.system.profile
e execute as consultas manualmente usandoexplain()
- Tente identificar as operações lentas no
explain()
saída, comoscanAndOrder
ounscanned
grande , etc. - 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.