web-dev-qa-db-fra.com

MongoDB utilisant trop de mémoire

Nous utilisons MongoDB depuis plusieurs semaines maintenant, la tendance générale que nous avons constatée est que mongodb utilise beaucoup trop de mémoire (bien plus que la taille totale de son ensemble de données + index).

J'ai déjà lu cette question et cette question , mais aucun ne semble résoudre le problème que j'ai rencontré, ils expliquent en fait ce qui est déjà expliqué dans la documentation .

Voici les résultats des commandes htop et show dbs.

enter image description here

show dbs

Je sais que mongodb utilise des IO mappés en mémoire, donc fondamentalement, le système d'exploitation gère les choses de mise en cache dans la mémoire, et mongodb devrait théoriquement lâcher sa mémoire mise en cache lorsqu'un autre processus demande de la mémoire libre , mais d'après ce que nous avons vu, ce n'est pas le cas.

Le MOO entre en action et commence à tuer d'autres processus importants, par exemple postgres, redis, etc. (Comme on peut le voir, pour surmonter ce problème, nous avons augmenté le RAM à 183 Go qui fonctionne maintenant mais est assez cher. mongo utilise ~ 87 Go de RAM, presque 4X de la taille de l'ensemble de ses données)

Donc,

  1. Cette utilisation de la mémoire est-elle vraiment attendue et normale? (Selon la documentation, WiredTiger utilise au maximum ~ 60% de RAM pour son cache, mais compte tenu de la taille de l'ensemble de données, dispose-t-il même de suffisamment de données pour pouvoir prendre 86 Go de RAM?)
  2. Même si l'utilisation de la mémoire est attendue, pourquoi mongo ne lâche-t-il pas sa mémoire allouée au cas où un autre processus commencerait à demander plus de mémoire? Divers autres processus en cours d'exécution étaient constamment tués par linux oom, y compris mongodb lui-même, avant d'augmenter le RAM et cela rendait le système totalement instable.

Merci !

29
SpiXel

D'accord, donc après avoir suivi les indices donnés par loicmathieu et jstell, et avoir creusé un peu, ce sont les choses que j'ai découvertes sur MongoDB en utilisant le moteur de stockage WiredTiger. Je le mets ici si quelqu'un a rencontré les mêmes questions.

Les threads d'utilisation de la mémoire que j'ai mentionnés appartenaient tous à 2012-2014, tous les WiredTiger antérieurs et décrivent le comportement du moteur de stockage MMAPV1 d'origine qui ne fonctionne pas 't ont un cache séparé ou un support pour la compression.

Le WiredTiger paramètres de cache contrôle uniquement la taille de la mémoire directement utilisée par le moteur de stockage WiredTiger (pas la mémoire totale utilisée par mongod). Beaucoup d'autres choses prennent potentiellement de la mémoire dans une configuration MongoDB/WiredTiger, comme les suivantes:

  • WiredTiger compresse le stockage sur disque, mais les données en mémoire ne sont pas compressées.

  • WiredTiger par défaut ne synchronise pas les données sur chaque commit , donc les fichiers journaux sont également dans RAM qui prend son Il est également mentionné que pour utiliser efficacement les E/S, WiredTiger regroupe les demandes d'E/S (cache manquant), ce qui semble également prendre quelques RAM (En fait, les pages sales (les pages qui ont changé/mis à jour) ont une liste de mises à jour sur elles stockées dans Concurrent SkipList ).

  • WiredTiger conserve plusieurs versions d'enregistrements dans son cache (Multi Version Concurrency Control, les opérations de lecture accèdent à la dernière version validée avant leur opération).

  • WiredTiger Conserve les sommes de contrôle des données dans le cache.

  • MongoDB lui-même consomme de la mémoire pour gérer les connexions ouvertes, les agrégations, le code côté serveur et etc. .

Compte tenu de ces faits, en s'appuyant sur show dbs; n'était pas techniquement correct, car il ne montre que la taille compressée des jeux de données.

Les commandes suivantes peuvent être utilisées afin d'obtenir la taille complète du jeu de données.

db.getSiblingDB('data_server').stats()
# OR
db.stats()

Ces résultats sont les suivants:

{
    "db" : "data_server",
    "collections" : 11,
    "objects" : 266565289,
    "avgObjSize" : 224.8413545621088,
    "dataSize" : 59934900658, # 60GBs
    "storageSize" : 22959984640,
    "numExtents" : 0,
    "indexes" : 41,
    "indexSize" : 7757348864, # 7.7GBs
    "ok" : 1
}

Il semble donc que la taille réelle de l'ensemble de données + ses index prennent environ 68 Go de cette mémoire.

Compte tenu de tout cela, je suppose que l'utilisation de la mémoire est maintenant assez attendue, bonne partie étant qu'il est tout à fait correct de limiter la taille du cache WiredTiger, car il gère les opérations d'E/S assez efficacement (comme décrit ci-dessus).

Il reste également le problème de MOO, pour surmonter ce problème, car nous n'avions pas suffisamment de ressources pour supprimer mongodb, nous avons réduit le oom_score_adj pour éviter MOO de tuer des processus importants pour le moment (ce qui signifie que nous avons dit MOO de ne pas tuer nos processus souhaités ).

23
SpiXel

Documents

Vous pouvez lire problèmes de mémoire de base pour MongoDB et aussi ceci brève discussion sur la vérification de l'utilisation de la mémoire .

Présentation de l'utilisation de la mémoire

La commande db.serverStatus() ( docs ) peut fournir un aperçu de l'utilisation de la mémoire, en particulier:

> db.serverStatus().mem
{ "bits" : 64, "resident" : 27, "virtual" : 397, "supported" : true }

> db.serverStatus().tcmalloc
... not easy to read! ...

> db.serverStatus().tcmalloc.tcmalloc.formattedString
------------------------------------------------
MALLOC:        3416192 (    3.3 MiB) Bytes in use by application
MALLOC: +      4788224 (    4.6 MiB) Bytes in page heap freelist
MALLOC: +       366816 (    0.3 MiB) Bytes in central cache freelist
...
... a bunch of stats in an easier to read format ...

Quelle est la taille de vos index?

db.stats() peut afficher la taille totale de tous les index, mais nous pouvons également obtenir des informations détaillées pour une seule collection en utilisant db.myCollection.stats()

Par exemple, cette commande comparera les tailles des index pour chaque collection :

> db.getCollectionNames().map(name => ({totalIndexSize: db.getCollection(name).stats().totalIndexSize, name: name})).sort((a, b) => a.totalIndexSize - b.totalIndexSize).forEach(printjson)
...
{ "totalIndexSize" : 696320, "name" : "smallCollection" }
{ "totalIndexSize" : 135536640, "name" : "bigCollection" }
{ "totalIndexSize" : 382681088, "name" : "hugeCollection" }
{ "totalIndexSize" : 511901696, "name" : "massiveCollection" }

Maintenant, nous pouvons regarder les détails de cette collection massive, pour voir lesquels de ses index sont les plus coûteux:

> db.massiveCollection.stats().indexSizes
{
        "_id_" : 230862848,
        "groupId_1_userId_1" : 49971200,
        "createTime_1" : 180301824,
        "orderId_1" : 278528,
        "userId_1" : 50155520
}

Cela peut nous donner une meilleure idée des économies possibles.

(Dans ce cas, nous avions un index sur createTime qui était plutôt énorme - une entrée par document - et nous avons décidé que nous pouvions vivre sans.)

4
joeytwiddle

Je ne pense pas que vous ayez un problème ici avec MongoDB, car jstell vous a dit que MongoDB avec WiredTiger utilisera 50% de la mémoire disponible, donc si vous augmentez le RAM de votre serveur, cela prendra plus de mémoire .

Comme c'est plus que la taille des index DB +, gardez à l'esprit que WiredTiger compresse la base de données sur le disque et utilise également des journaux d'instantanés pour enregistrer les modifications de document. Ainsi, la taille réelle du WiredTiger est la taille utilisant show dbs * compression_ration + taille des journaux d'instantanés. Il est donc presque impossible de connaître la taille exacte attendue.

Gardez également à l'esprit que des outils comme top, ps, htop n'affichaient pas la mémoire réellement utilisée par l'application, reportez-vous à cette question SOW pour plus de détails: https://stackoverflow.com/questions/131303/how-to-measure-actual-memory-usage-of-an-application-or-process

Maintenant, revenons à votre problème. Vous avez d'autres outils en cours d'exécution sur le même hôte et un MOO les tue. Je ne connais pas Linux OOM mais êtes-vous sûr qu'il tue ceux-ci à cause de MongoDB ou .. juste à cause d'eux (peut-être qu'il tue Postgres parce que Postgres a pris trop de mémoire).

Quoi qu'il en soit, comme meilleure pratique si vous avez une grande base de données Mongo, ne l'installez pas dans un hôte partagé avec d'autres bases de données ou vous aurez beaucoup de difficultés, au cas où il y aurait un problème comme celui que vous décrivez ici, à savoir qui causent vraiment le problème sur l'hôte.

4
loicmathieu