MongoDB utilizza troppa memoria


28

Usiamo MongoDB da diverse settimane, la tendenza generale che abbiamo visto è stata che mongodb sta usando troppa memoria (molto più dell'intera dimensione del suo set di dati + indici).

Ho già letto questa domanda e questa domanda , ma nessuna sembra affrontare il problema che ho riscontrato, in realtà stanno spiegando ciò che è già stato spiegato nella documentazione.

I seguenti sono i risultati di htop e mostrano i comandi dbs .

inserisci qui la descrizione dell'immagine

mostra dbs

So che mongodb utilizza IO mappato in memoria, quindi sostanzialmente il sistema operativo gestisce la memorizzazione nella cache delle cose nella memoria, e mongodb dovrebbe teoricamente lasciare andare la sua memoria cache quando un altro processo richiede memoria libera , ma da quello che abbiamo visto, non lo fa.

OOM inizia a uccidere altri importanti processi come postgres, redis, ecc. (Come si può vedere, per ovviare a questo problema, abbiamo aumentato la RAM a 183 GB che ora funziona ma è piuttosto costoso. Mongo usa ~ 87 GB di RAM, quasi 4 volte la dimensione dell'intero set di dati)

Così,

  1. Questo uso della memoria è davvero previsto e normale? (Come da documentazione, WiredTiger utilizza al massimo il ~ 60% di RAM per la sua cache, ma considerando le dimensioni del set di dati, ha anche abbastanza dati per essere in grado di occupare 86 GB di RAM?)
  2. Anche se è previsto l'utilizzo della memoria, perché mongo non lascerà andare la sua memoria allocata nel caso in cui un altro processo inizi a richiedere più memoria? Vari altri processi in esecuzione venivano costantemente uccisi da Linux Oom, incluso lo stesso mongodb, prima di aumentare la RAM e rendere il sistema totalmente instabile.

Grazie !


4
Forse alcune delle presentazioni sugli interni di WiredTiger, come mongodb.com/presentations/… , possono far luce. Mi aspetto che l'utilizzo predefinito del 50% della RAM fisica sia solo un'ipotesi di ciò che è probabilmente richiesto su un host MongoDB dedicato e molti dovranno cambiarlo. FWIW, non credo che l'impostazione di cacheSizeGB sia "limitante" mongo - l'opzione è lì, quindi hai il controllo sulle distribuzioni. Determinare la quantità di memoria che mongo "necessita" per la cache richiederebbe di monitorare le statistiche della cache del server con il carico del server previsto.

Risposte:


23

Ok, quindi dopo aver seguito gli indizi forniti da loicmathieu e jstell e averlo scavato un po ', queste sono le cose che ho scoperto su MongoDB usando il motore di archiviazione WiredTiger. Lo metto qui se qualcuno ha incontrato le stesse domande.

I thread sull'utilizzo della memoria che ho citato appartengono tutti al 2012-2014, tutti WiredTiger precedenti e descrivono il comportamento del motore di archiviazione MMAPV1 originale che non ha una cache separata o supporto per la compressione.

Le impostazioni della cache di WiredTiger controllano solo la dimensione della memoria utilizzata direttamente dal motore di archiviazione WiredTiger (non la memoria totale utilizzata da mongod). Molte altre cose stanno potenzialmente prendendo la memoria in una configurazione MongoDB / WiredTiger, come la seguente:

  • WiredTiger comprime l'archiviazione su disco, ma i dati in memoria non sono compressi.

  • WiredTiger per impostazione predefinita non sincronizza i dati su ciascun commit , quindi anche i file di registro si trovano nella RAM, il che richiede un tributo in memoria. Si dice anche che, al fine di utilizzare l'I / O in modo efficiente, WiredTiger blocca insieme le richieste di I / O (mancate cache), che sembra richiedere anche un po 'di RAM (le pagine sporche (pagine che sono state modificate / aggiornate) hanno un elenco di aggiornamenti memorizzati su un SkipList concorrente ).

  • WiredTiger mantiene più versioni dei record nella sua cache (Controllo concorrenza multi versione, le operazioni di lettura accedono all'ultima versione impegnata prima della loro operazione).

  • WiredTiger Mantiene i checksum dei dati nella cache.

  • MongoDB stesso consuma memoria per gestire connessioni aperte, aggregazioni, codice lato server ed ecc .

Considerando questi fatti, fare affidamento show dbs;non era tecnicamente corretto, poiché mostra solo la dimensione compressa dei set di dati.

I seguenti comandi possono essere utilizzati per ottenere l'intera dimensione del set di dati.

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

Questo risultato è il seguente:

{
    "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
}

Quindi sembra che la dimensione effettiva del set di dati + i suoi indici stiano occupando circa 68 GB di quella memoria.

Considerando tutto ciò, immagino che l'utilizzo della memoria sia ora abbastanza previsto, buona parte del fatto che è del tutto ok limitare la dimensione della cache di WiredTiger, poiché gestisce le operazioni di I / O in modo abbastanza efficiente (come descritto sopra).

Rimane anche il problema di OOM, per ovviare a questo problema, dal momento che non avevamo abbastanza risorse per eliminare mongodb, abbiamo abbassato oom_score_adj per impedire a OOM di uccidere processi importanti per il momento (significa che abbiamo detto a OOM di non uccidere il nostro processi desiderati ).


Abbiamo un problema simile. MongoDB continua a mangiare RAM. Proporzioni simili. La oom_score_adj soluzione è stata la cosa migliore che riesci a trovare?
Hartator,

@Hartator Beh, abbiamo ridotto la cacheSiger di wiredtiger, ci siamo impegnati di più nella gestione dei nostri indici e della nostra politica di indicizzazione e, infine, abbiamo ridotto oom_score_adj per le cose che ci interessavano, suppongo che tutto ciò che si possa fare comunque.
SpiXel l'

4

Non credo che tu abbia un problema qui con MongoDB, poiché jstell ti ha detto che MongoDB con WiredTiger utilizzerà il 50% della memoria disponibile, quindi se aumenti la RAM del tuo server ci vorrà più memoria.

Perché è più della dimensione degli indici DB +, tieni presente che WiredTiger comprime il database su disco e usa anche i log delle istantanee per registrare le modifiche al documento. Quindi la dimensione reale di WiredTiger è la dimensione utilizzando show dbs * compressione_razione + dimensione dei registri di istantanee. Quindi è quasi impossibile conoscere l'esatta dimensione prevista.

Anche tenere a mente che strumenti come top, ps, htopnon ha visualizzare la memoria realmente utilizzato dall'applicazione, refere a questa domanda SOW per i dettagli: /programming/131303/how-to-measure-actual-memory -usage-di-un-application-o-process

Ora, torniamo al tuo problema. Hai altri strumenti in esecuzione sullo stesso host e una OOM li uccide. Non ho familiarità con Linux OOM ma sei sicuro che uccida quelli a causa di MongoDB o ... solo per loro (forse uccide Postgres perché Postgres ha preso troppa memoria).

Comunque, come best practice se hai un grande database Mongo, non installarlo in un host condiviso con altri database o avrai molte difficoltà, nel caso ci sia un problema come quello che descrivi qui, per sapere che causano davvero il problema sull'host.


4

Documenti

Potresti leggere le preoccupazioni di base sulla memoria di MongoDB e anche questa breve discussione sul controllo dell'utilizzo della memoria .

Panoramica sull'utilizzo della memoria

Il comando db.serverStatus()( documenti ) può fornire una panoramica dell'utilizzo della memoria, in particolare:

> 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 ...

Quanto sono grandi i tuoi indici?

db.stats() può mostrare la dimensione totale di tutti gli indici, ma possiamo anche ottenere informazioni dettagliate per una singola raccolta utilizzando db.myCollection.stats()

Ad esempio, questo comando confronta le dimensioni degli indici per ogni raccolta :

> 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" }

Ora possiamo guardare i dettagli di quella vasta collezione, per vedere quali dei suoi indici sono i più costosi:

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

Questo può darci un'idea migliore di dove potrebbero essere possibili risparmi.

(In questo caso, avevamo un indice su createTimecui era piuttosto enorme - una voce per documento - e abbiamo deciso che potremmo vivere senza di essa.)


Gli indici hanno un grande costo di memoria?
Mathias Lykkegaard Lorenzen il

@MathiasLykkegaardLorenzen Dipende dal numero di valori univoci per il campo che hai indicizzato, rispetto alla RAM del tuo server. Nel nostro caso, l' createTimeindice era problematico perché era unico per ogni singolo documento e quella raccolta era enorme. L'indicizzazione degli altri campi era ok, perché c'erano meno valori univoci (i valori erano raggruppati).
joeytwiddle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.