MongoDB usando muita memória

28

Estamos usando o MongoDB há várias semanas, a tendência geral que vimos foi que o mongodb está usando muita memória (muito mais do que todo o tamanho de seu conjunto de dados + índices).

Eu já li essa pergunta e essa pergunta , mas nenhuma parece abordar o problema que estou enfrentando; na verdade, elas estão explicando o que já foi explicado na documentação.

A seguir estão os resultados dos comandos htop e show dbs .

insira a descrição da imagem aqui

show dbs

Eu sei que o mongodb usa E / S mapeada na memória, portanto, basicamente, o sistema operacional lida com o armazenamento em cache de coisas na memória, e o mongodb deveria, teoricamente, deixar sua memória em cache quando outro processo solicitar memória livre , mas pelo que vimos, isso não acontece.

OOM começa e acaba com outros processos importantes, como postgres, redis etc. (Como pode ser visto, para superar esse problema, aumentamos a RAM para 183 GB, que agora funciona, mas é bastante cara. O mongo está usando ~ 87 GB de RAM, quase 4X do tamanho de todo o conjunto de dados)

Tão,

  1. Essa quantidade de memória é realmente esperada e normal? (De acordo com a documentação, o WiredTiger usa no máximo ~ 60% da RAM para seu cache, mas considerando o tamanho do conjunto de dados, ele possui dados suficientes para conseguir 86 GB de RAM?)
  2. Mesmo que o uso de memória seja esperado, por que o mongo não larga sua memória alocada, caso outro processo comece a solicitar mais memória? Vários outros processos em execução estavam sendo constantemente eliminados pelo linux oom, incluindo o próprio mongodb, antes de aumentarmos a RAM e tornar o sistema totalmente instável.

Obrigado !

SpiXel
fonte
4
Talvez algumas das apresentações internas do WiredTiger, como mongodb.com/presentations/… , possam esclarecer alguma coisa. Espero que o uso padrão de 50% da RAM física seja apenas um palpite sobre o que é provavelmente necessário em um host MongoDB dedicado, e muitos precisarão alterá-lo. FWIW, não acredito que definir o cacheSizeGB esteja "limitando" o mongo - a opção existe para que você tenha controle sobre as implantações. Determinar quanta memória o mongo "precisa" para o cache exigiria o monitoramento das estatísticas de cache do servidor sob a carga esperada do servidor.

Respostas:

23

Ok, depois de seguir as dicas dadas por loicmathieu e jstell e desenterrá-las um pouco, essas são as coisas que descobri sobre o MongoDB usando o mecanismo de armazenamento WiredTiger. Estou colocando aqui se alguém encontrou as mesmas perguntas.

Os threads de uso de memória que mencionei pertenceram a 2012-2014, todos anteriores ao WiredTiger e estão descrevendo o comportamento do mecanismo de armazenamento MMAPV1 original que não possui um cache ou suporte à compactação separado.

As configurações de cache do WiredTiger controlam apenas o tamanho da memória usada diretamente pelo mecanismo de armazenamento WiredTiger (não a memória total usada pelo mongod). Muitas outras coisas estão potencialmente ocupando memória em uma configuração do MongoDB / WiredTiger, como as seguintes:

  • O WiredTiger compacta o armazenamento em disco, mas os dados na memória não são compactados.

  • O WiredTiger, por padrão, não sincroniza os dados em cada confirmação , portanto os arquivos de log também estão na RAM, o que afeta sua memória. Também é mencionado que, para usar E / S com eficiência, os WiredTiger dividem as solicitações de E / S (falhas de cache) juntas, o que também parece consumir RAM (na verdade, páginas sujas (páginas que foram alteradas / atualizadas) têm uma lista de atualizações armazenadas em uma SkipList simultânea ).

  • O WiredTiger mantém várias versões de registros em seu cache (controle de simultaneidade de várias versões , as operações de leitura acessam a última versão confirmada antes da operação).

  • WiredTiger Mantém as somas de verificação dos dados no cache.

  • -Se MongoDB consome memória para lidar com conexões abertas, agregações, código serverside e etc .

Considerando esses fatos, confiar show dbs;não era tecnicamente correto, pois mostra apenas o tamanho compactado dos conjuntos de dados.

Os seguintes comandos podem ser usados ​​para obter o tamanho completo do conjunto de dados.

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

Este resultado é o seguinte:

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

Portanto, parece que o tamanho real do conjunto de dados + seus índices estão consumindo cerca de 68 GB dessa memória.

Considerando tudo isso, acho que o uso de memória agora é bastante esperado, sendo uma boa parte limitar completamente o tamanho do cache do WiredTiger, já que ele lida com operações de E / S com bastante eficiência (como descrito acima).

Também existe o problema da OOM, para superar esse problema, como não tínhamos recursos suficientes para remover o mongodb, reduzimos o oom_score_adj para impedir que a OOM matasse processos importantes por enquanto (o que significa que dissemos à OOM para não matar nossa processos desejados ).

SpiXel
fonte
Temos um problema semelhante. O MongoDB continua consumindo RAM. Proporções semelhantes. A oom_score_adj solução foi a melhor coisa que você conseguiu criar?
Hartator 28/09
@Hartator Bem, diminuímos o cacheSize da wiredtiger, dedicamos mais esforços no gerenciamento de nossos índices e na política de indexação e, finalmente, diminuímos oom_score_adj para as coisas que cuidamos, acho que tudo isso pode ser feito de qualquer maneira.
SpiXel
4

Eu não acho que você tenha um problema aqui com o MongoDB, como jstell disse que o MongoDB com WiredTiger usará 50% da memória disponível; portanto, se você aumentar a RAM do seu servidor, será necessário mais memória.

Por que é mais do que o tamanho dos índices DB +, lembre-se de que o WiredTiger compacta o banco de dados no disco e também usa logs de instantâneo para registrar alterações de documentos. Portanto, o tamanho real do WiredTiger é o tamanho usando show dbs *pression_ration + tamanho dos logs de captura instantânea. Portanto, é quase impossível saber o tamanho exato esperado.

Mantenha também em mente que ferramentas como top, ps, htopnão exibir a memória realmente usada pelo aplicativo,, comunique a esta pergunta SOW para mais detalhes: https://stackoverflow.com/questions/131303/how-to-measure-actual-memory -usage-of-an-application-or-process

Agora, volte ao seu problema. Você tem outras ferramentas em execução no mesmo host e um OOM as mata. Eu não estou familiarizado com o Linux OOM, mas você tem certeza de que ele os mata por causa do MongoDB ou .. apenas por causa deles (talvez mate o Postgres porque o Postgres consumiu muita memória).

De qualquer forma, como prática recomendada se você tiver um grande banco de dados Mongo, não o instale em um host compartilhado com outros bancos de dados ou você terá muitas dificuldades, caso haja um problema como o que você descreve aqui, para saber que realmente causam o problema no host.

loicmathieu
fonte
4

Documentos

Você pode ler as preocupações básicas de memória do MongoDB e também esta breve discussão sobre a verificação do uso da memória .

Visão geral do uso de memória

O comando db.serverStatus()( docs ) pode fornecer uma visão geral do uso da memória, especificamente:

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

Qual é o tamanho dos seus índices?

db.stats() pode mostrar o tamanho total de todos os índices, mas também podemos obter informações detalhadas de uma única coleção usando db.myCollection.stats()

Por exemplo, este comando irá comparar os tamanhos dos índices para cada coleção :

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

Agora podemos ver os detalhes dessa coleção massiva, para ver quais de seus índices são os mais caros:

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

Isso pode nos dar uma idéia melhor de onde é possível economizar.

(Nesse caso, tínhamos um índice createTimebastante grande - uma entrada por documento - e decidimos que poderíamos viver sem ele.)

joeytwiddle
fonte
Os índices têm um grande custo de memória?
Mathias Lykkegaard Lorenzen 25/03
@MathiasLykkegaardLorenzen Depende do número de valores exclusivos para o campo que você indexou, em relação à RAM do servidor. No nosso caso, o createTimeíndice era problemático porque era único para cada documento e essa coleção era enorme. A indexação dos outros campos estava correta, porque havia menos valores exclusivos (os valores foram agrupados).
joeytwiddle 26/03