MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Performances d'interrogation de MongoDB pour plus de 5 millions d'enregistrements

C'est chercher l'aiguille dans une botte de foin. Nous aurions besoin d'une sortie de explain() pour les requêtes qui ne fonctionnent pas bien. Malheureusement, même cela ne résoudrait le problème que pour cette requête particulière, alors voici une stratégie sur la façon d'aborder cela :

  1. Assurez-vous que ce n'est pas à cause d'une RAM insuffisante et d'une pagination excessive
  2. Activer le profileur de base de données (à l'aide de db.setProfilingLevel(1, timeout)timeout est le seuil du nombre de millisecondes que prend la requête ou la commande, tout ce qui est plus lent sera enregistré)
  3. Inspectez les requêtes lentes dans db.system.profile et exécutez les requêtes manuellement en utilisant explain()
  4. Essayez d'identifier les opérations lentes dans explain() sortie, telle que scanAndOrder ou grand nscanned , etc.
  5. Raison concernant la sélectivité de la requête et s'il est possible d'améliorer la requête à l'aide d'un index du tout . Si ce n'est pas le cas, envisagez de désactiver le paramètre de filtre pour l'utilisateur final ou affichez-lui une boîte de dialogue d'avertissement indiquant que l'opération pourrait être lente.

Un problème clé est que vous autorisez apparemment vos utilisateurs à combiner des filtres à volonté. Sans intersection d'index, cela augmentera considérablement le nombre d'index requis.

De plus, lancer aveuglément un index à chaque requête possible est une très mauvaise stratégie. Il est important de structurer les requêtes et de s'assurer que les champs indexés ont une sélectivité suffisante .

Supposons que vous ayez une requête pour tous les utilisateurs avec status "actif" et quelques autres critères. Mais sur les 5 millions d'utilisateurs, 3 millions sont actifs et 2 millions ne le sont pas, donc sur 5 millions d'entrées, il n'y a que deux valeurs différentes. Un tel index n'aide généralement pas. Il est préférable de rechercher d'abord les autres critères, puis de scanner les résultats. En moyenne, lors du retour de 100 documents, vous devrez numériser 167 documents, ce qui ne nuira pas trop aux performances. Mais ce n'est pas si simple. Si le critère principal est le joined_at date de l'utilisateur et la probabilité que les utilisateurs cessent d'utiliser avec le temps est élevée, vous pourriez finir par devoir scanner des milliers de documents avant de trouver une centaine de correspondances.

L'optimisation dépend donc beaucoup des données (pas seulement de leur structure , mais aussi les données elles-mêmes ), ses corrélations internes et vos modèles de requête .

Les choses s'aggravent lorsque les données sont trop volumineuses pour la RAM, car alors, avoir un index est génial, mais l'analyse (ou même simplement le retour) des résultats peut nécessiter la récupération aléatoire d'un grand nombre de données sur le disque, ce qui prend beaucoup de temps. /P>

La meilleure façon de contrôler cela est de limiter le nombre de types de requêtes différents, d'interdire les requêtes sur des informations peu sélectives et d'essayer d'empêcher l'accès aléatoire aux anciennes données.

Si tout le reste échoue et si vous avez vraiment besoin d'autant de flexibilité dans les filtres, il peut être intéressant d'envisager une base de données de recherche distincte qui prend en charge les intersections d'index, de récupérer les identifiants mongo à partir de là, puis d'obtenir les résultats de mongo en utilisant $in . Mais cela comporte ses propres périls.

-- MODIFIER --

L'explication que vous avez publiée est un bel exemple d'un problème lié à la numérisation de champs à faible sélectivité. Apparemment, il y a beaucoup de documents pour "[email protected]". Maintenant, trouver ces documents et les trier par horodatage est assez rapide, car il est pris en charge par des index à haute sélectivité. Malheureusement, comme il n'y a que deux types d'appareils, mongo doit numériser 30060 documents pour trouver le premier qui correspond à "mobile".

Je suppose qu'il s'agit d'une sorte de suivi Web et que le modèle d'utilisation de l'utilisateur ralentit la requête (s'il changeait de mobile et de Web quotidiennement, la requête serait rapide).

L'accélération de cette requête particulière peut être effectuée à l'aide d'un index composé contenant le type de périphérique, par ex. en utilisant

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

ou

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

Malheureusement, cela signifie que des requêtes comme find({"username" : "foo"}).sort({"timestamp" : -1}); ne peut plus utiliser le même index, donc, comme décrit, le nombre d'index augmentera très rapidement.

Je crains qu'il n'y ait pas de très bonne solution pour cela en utilisant mongodb pour le moment.