MongoDB usando demasiada memoria

28

Hemos estado usando MongoDB durante varias semanas, la tendencia general que hemos visto es que mongodb está usando demasiada memoria (mucho más que el tamaño total de su conjunto de datos + índices).

Ya leí esta pregunta y esta pregunta , pero ninguna parece abordar el problema que he enfrentado, en realidad están explicando lo que ya se explicó en la documentación.

Los siguientes son los resultados de los comandos htop y show dbs .

ingrese la descripción de la imagen aquí

mostrar dbs

Sé que mongodb usa IO mapeado en memoria, por lo que básicamente el sistema operativo maneja el almacenamiento en caché de las cosas en la memoria, y teóricamente, mongodb debería abandonar su memoria en caché cuando otro proceso solicita memoria libre , pero por lo que hemos visto, no lo hace.

OOM comienza a matar otros procesos importantes, por ejemplo, postgres, redis, etc. (Como se puede ver, para superar este problema, hemos aumentado la RAM a 183 GB, que ahora funciona pero es bastante costoso. Mongo está usando ~ 87 GB de ram, casi 4 veces el tamaño de todo su conjunto de datos)

Asi que,

  1. ¿Es este uso de memoria realmente esperado y normal? (Según la documentación, WiredTiger utiliza como máximo ~ 60% de RAM para su caché, pero teniendo en cuenta el tamaño del conjunto de datos, ¿tiene incluso suficientes datos para poder tomar 86 GB de RAM?)
  2. Incluso si se espera el uso de la memoria, ¿por qué Mongo no soltará su memoria asignada en caso de que otro proceso comience a solicitar más memoria? Linux oom estaba matando constantemente otros procesos en ejecución, incluido mongodb, antes de aumentar la RAM y hacer que el sistema fuera totalmente inestable.

Gracias !

SpiXel
fuente
44
Quizás algunas de las presentaciones sobre componentes internos de WiredTiger, como mongodb.com/presentations/… , puedan arrojar algo de luz. Espero que el uso predeterminado del 50% de la RAM física sea solo una suposición de lo que probablemente se requiere en un host MongoDB dedicado, y muchos necesitarán cambiarlo. FWIW, no creo que configurar el cacheSizeGB sea "limitante" mongo: la opción está ahí para que tenga control sobre las implementaciones. Determinar cuánta memoria "necesita" mongo para la memoria caché requeriría que supervise las estadísticas de la memoria caché del servidor bajo la carga esperada del servidor.

Respuestas:

23

De acuerdo, después de seguir las pistas dadas por loicmathieu y jstell, y desenterrar un poco, estas son las cosas que descubrí sobre MongoDB usando el motor de almacenamiento WiredTiger. Lo pongo aquí si alguien encuentra las mismas preguntas.

Los subprocesos de uso de memoria que mencioné, todos pertenecían a 2012-2014, todos anteriores a WiredTiger y describen el comportamiento del motor de almacenamiento MMAPV1 original que no tiene un caché separado o soporte para la compresión.

La configuración de caché de WiredTiger solo controla el tamaño de la memoria directamente utilizada por el motor de almacenamiento WiredTiger (no la memoria total utilizada por mongod). Potencialmente, muchas otras cosas están tomando memoria en una configuración MongoDB / WiredTiger, como las siguientes:

  • WiredTiger comprime el almacenamiento en disco, pero los datos en la memoria no están comprimidos.

  • WiredTiger por defecto no sincroniza los datos en cada confirmación , por lo que los archivos de registro también están en la RAM, lo que afecta la memoria. También se menciona que para usar E / S de manera eficiente, WiredTiger agrupa las solicitudes de E / S (errores de caché) juntas, eso también parece tomar algo de RAM (de hecho, las páginas sucias (páginas que han cambiado / actualizado) tienen una lista de actualizaciones en ellos almacenados en una SkipList concurrente ).

  • WiredTiger mantiene múltiples versiones de registros en su caché (Control de concurrencia de versiones múltiples, las operaciones de lectura acceden a la última versión confirmada antes de su operación)

  • WiredTiger Mantiene sumas de verificación de los datos en caché.

  • MongoDB sí consume memoria para manejar las conexiones abiertas, agregaciones, código serverside y etc .

Teniendo en cuenta estos hechos, confiar en él show dbs;no era técnicamente correcto, ya que solo muestra el tamaño comprimido de los conjuntos de datos.

Los siguientes comandos se pueden utilizar para obtener el tamaño completo del conjunto de datos.

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

Este resultado es el siguiente:

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

Por lo tanto, parece que el tamaño real del conjunto de datos + sus índices están tomando aproximadamente 68 GB de esa memoria.

Teniendo en cuenta todo esto, supongo que el uso de la memoria ahora es bastante esperado, buena parte es que está completamente bien limitar el tamaño de la caché WiredTiger, ya que maneja las operaciones de E / S de manera bastante eficiente (como se describió anteriormente).

También queda el problema de OOM, para superar este problema, ya que no teníamos suficientes recursos para eliminar mongodb, bajamos el oom_score_adj para evitar que OOM matara procesos importantes por el momento (lo que significa que le dijimos a OOM que no mate a nuestro procesos deseados ).

SpiXel
fuente
Tenemos un problema similar. MongoDB sigue comiendo RAM. Proporciones similares ¿Fue la oom_score_adj solución lo mejor que se te ocurrió?
Hartator
@Hartator Bueno, disminuimos el tamaño de caché de wiredtiger, hicimos más esfuerzos para administrar nuestros índices y la política de indexación, y finalmente, disminuimos oom_score_adj para las cosas que nos importaban, eso supongo que es todo lo que se puede hacer de todos modos.
SpiXel
4

No creo que tenga un problema aquí con MongoDB, ya que jstell le dijo que MongoDB con WiredTiger usará el 50% de la memoria disponible, por lo que si aumenta la RAM de su servidor, necesitará más memoria.

Por qué es más grande que el tamaño de los índices DB +, tenga en cuenta que WiredTiger comprime la base de datos en el disco y también utiliza registros de instantáneas para registrar los cambios en el documento. Por lo tanto, el tamaño real de WiredTiger es el tamaño que usa show dbs * compress_ration + size de los registros de instantáneas. Por lo tanto, es casi imposible saber el tamaño exacto esperado.

También hay que tener en cuenta que las herramientas como top, ps, htopno mostrar la memoria realmente utilizada por la aplicación, refere a esta pregunta Declaración de trabajo para más detalles: https://stackoverflow.com/questions/131303/how-to-measure-actual-memory -uso-de-una-aplicación-o-proceso

Ahora, volviendo a su problema. Tiene otras herramientas ejecutándose en el mismo host y un OOM las mata. No estoy familiarizado con Linux OOM, pero ¿estás seguro de que los mata por MongoDB o ... solo por ellos (tal vez mata a Postgres porque Postgres tomó demasiada memoria)?

De todos modos, como práctica recomendada si tiene una gran base de datos Mongo, no la instale en un host compartido con otras bases de datos o tendrá muchas dificultades, en caso de que haya un problema como el que describe aquí, para saber quien realmente causa el problema en el host.

Loicmathieu
fuente
4

Docs

Es posible que desee leer las preocupaciones básicas de memoria para MongoDB y también esta breve discusión sobre cómo verificar el uso de la memoria .

Resumen de uso de memoria

El comando db.serverStatus()( docs ) puede proporcionar una descripción general del uso de la memoria, específicamente:

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

¿Qué tan grandes son sus índices?

db.stats() puede mostrar el tamaño total de todos los índices, pero también podemos obtener información detallada para una sola colección usando db.myCollection.stats()

Por ejemplo, este comando comparará los tamaños de los índices para cada colección :

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

Ahora podemos ver los detalles de esa colección masiva, para ver cuáles de sus índices son los más costosos:

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

Esto nos puede dar una mejor idea de dónde podrían ser posibles los ahorros.

(En este caso, teníamos un índice sobre el createTimecual era bastante grande, una entrada por documento, y decidimos que podíamos vivir sin él).

joeytwiddle
fuente
¿Los índices tienen un gran costo de memoria?
Mathias Lykkegaard Lorenzen
@MathiasLykkegaardLorenzen Depende del número de valores únicos para el campo que ha indexado, en relación con la RAM de su servidor. En nuestro caso, el createTimeíndice era problemático porque era único para cada documento y esa colección era enorme. La indexación de los otros campos estaba bien, porque había menos valores únicos (los valores estaban agrupados).
joeytwiddle