MongoDB использует слишком много памяти


28

Мы используем MongoDB уже несколько недель, общая тенденция, которую мы видели, заключается в том, что mongodb использует слишком много памяти (намного больше, чем весь размер его набора данных + индексы).

Я уже прочитал этот вопрос и этот вопрос , но, похоже, никто не решает проблему, с которой я столкнулся, они фактически объясняют то, что уже объяснено в документации.

Ниже приведены результаты команд htop и show dbs .

введите описание изображения здесь

показать дбс

Я знаю, что mongodb использует IO с отображением в памяти, поэтому в основном ОС обрабатывает кэширование в памяти, и mongodb должен теоретически освобождать свою кэшированную память, когда другой процесс запрашивает свободную память , но, как мы видели, это не так.

OOM начинает работу, убивая другие важные процессы, такие как postgres, redis и т. Д. (Как можно видеть, чтобы преодолеть эту проблему, мы увеличили объем ОЗУ до 183 ГБ, который сейчас работает, но довольно дорогой. Монго использует ~ 87 ГБ ОЗУ, почти в 4 раза больше, чем весь его набор данных)

Так,

  1. Действительно ли такое потребление памяти ожидается и нормально? (Согласно документации, WiredTiger использует максимум ~ 60% оперативной памяти для своего кэша, но, учитывая размер набора данных, достаточно ли данных для того, чтобы можно было использовать 86 ГБ оперативной памяти?)
  2. Даже если ожидается использование памяти, почему mongo не отпустит выделенную память, если другой процесс начнет запрашивать больше памяти? Различные другие запущенные процессы были постоянно убиты linux oom, включая сам mongodb, прежде чем мы увеличили объем оперативной памяти, и это сделало систему совершенно нестабильной.

Благодарность !


4
Возможно, некоторые из презентаций по внутренним компонентам WiredTiger, такие как mongodb.com/presentations/… , могут пролить некоторый свет. Я ожидаю, что использование по умолчанию 50% физической памяти - это всего лишь предположение о том, что, вероятно, требуется на выделенном хосте MongoDB, и многим потребуется его изменить. FWIW, я не верю, что установка cacheSizeGB является «ограничивающей» монго - опция есть, поэтому вы можете контролировать развертывания. Чтобы определить, сколько памяти mongo «требует» для кэша, вам потребуется отслеживать статистику кэша сервера при ожидаемой нагрузке на сервер.

Ответы:


23

Итак, после следования подсказкам, данным loicmathieu и jstell, и немного покопав их, вот что я узнал о MongoDB, используя механизм хранения WiredTiger. Я ставлю это здесь, если кто-то сталкивался с такими же вопросами.

Потоки памяти, о которых я упоминал, все принадлежали 2012-2014 годам , все предшествующие WiredTiger и описывают поведение исходного механизма хранения MMAPV1, который не имеет отдельного кэша или поддержки сжатия.

Настройки кэша WiredTiger контролируют только объем памяти, непосредственно используемый механизмом хранения WiredTiger (но не общий объем памяти, используемый mongod). Многие другие вещи могут занимать память в конфигурации MongoDB / WiredTiger, например:

  • WiredTiger сжимает дисковое хранилище, но данные в памяти не сжимаются.

  • WiredTiger по умолчанию не синхронизирует данные при каждом коммите , поэтому файлы журнала также находятся в оперативной памяти, что сказывается на памяти. Также упоминается, что для эффективного использования операций ввода-вывода WiredTiger объединяет запросы ввода-вывода (ошибки кэширования), что также требует некоторой оперативной памяти (на самом деле грязные страницы (страницы, которые были изменены / обновлены) имеют список обновлений). на них хранятся в параллельном SkipList ).

  • WiredTiger хранит несколько версий записей в своем кэше (управление одновременной версией нескольких версий, операции чтения обращаются к последней подтвержденной версии перед их работой).

  • WiredTiger Сохраняет контрольные суммы данных в кеше.

  • Сам MongoDB использует память для обработки открытых соединений, агрегатов, серверного кода и т . Д.

Учитывая эти факты, полагаться на show dbs;это не было технически правильно, так как он показывает только сжатый размер наборов данных.

Следующие команды могут использоваться для получения полного размера набора данных.

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

Эти результаты следующие:

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

Таким образом, кажется, что фактический размер набора данных + его индексы занимают около 68 ГБ этой памяти.

Учитывая все это, я предполагаю, что использование памяти теперь вполне ожидаемое, хорошая часть в том, что вполне нормально ограничить размер кэша WiredTiger, поскольку он довольно эффективно обрабатывает операции ввода-вывода (как описано выше).

Также остается проблема OOM, чтобы преодолеть эту проблему, так как у нас не было достаточно ресурсов, чтобы убрать mongodb, мы понизили oom_score_adj, чтобы OOM не убивал важные процессы на данный момент (то есть мы сказали OOM не убивать наши желаемые процессы ).


У нас похожая проблема. MongoDB продолжает поглощать оперативную память. Схожие пропорции. Было ли oom_score_adj решение лучшим, что вам удалось придумать?
Хартатор,

@Hartator Что ж, мы сократили cacheSize wiredtiger, приложили больше усилий для управления нашими индексами и политикой индексирования, а затем, наконец, уменьшили oom_score_adj для вещей, о которых мы заботились, и я думаю, все, что можно сделать в любом случае.
SpiXel

4

Я не думаю, что у вас есть проблема с MongoDB, так как jstell сказал, что MongoDB с WiredTiger будет использовать 50% доступной памяти, поэтому, если вы увеличите объем оперативной памяти вашего сервера, потребуется больше памяти.

Поэтому размер индексов DB + превышает размер, имейте в виду, что WiredTiger сжимает базу данных на диске, а также использует журналы моментальных снимков для записи изменений документа. Таким образом, реальный размер WiredTiger - это размер с использованием show dbs * compress_ration + size журналов снимков. Так что почти невозможно узнать точный ожидаемый размер.

Имейте также в виду , что такие инструменты , как top, ps, htopне проявляла память действительно , используемый приложением, refere к этому SOW вопрос для деталей: /programming/131303/how-to-measure-actual-memory -usage-оф-ан-приложения или-процесс

Теперь вернемся к вашей проблеме. У вас есть другие инструменты, работающие на том же хосте, и OOM убивает их. Я не знаком с Linux OOM, но вы уверены, что он убивает тех из-за MongoDB или ... просто из-за них (возможно, это убивает Postgres, потому что Postgres занимал слишком много памяти).

В любом случае, если у вас есть большая база данных Mongo, лучше не устанавливать ее на хосте, совместно используемом с другими базами данных, иначе у вас возникнет много трудностей, если возникнет проблема, подобная той, которую вы описали здесь, кто действительно вызывает проблему на хосте.


4

Документы

Вы можете прочитать основные проблемы с памятью для MongoDB, а также это краткое обсуждение о проверке использования памяти .

Обзор использования памяти

Команда db.serverStatus()( docs ) может предоставить обзор использования памяти, а именно:

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

Насколько велики ваши показатели?

db.stats() может показать общий размер всех индексов, но мы также можем получить подробную информацию для одной коллекции, используя db.myCollection.stats()

Например, эта команда будет сравнивать размеры индексов для каждой коллекции :

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

Теперь мы можем взглянуть на детали этой огромной коллекции, чтобы увидеть, какие из ее индексов являются самыми дорогостоящими:

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

Это может дать нам лучшее представление о том, где возможна экономия.

(В этом случае у нас createTimeбыл довольно большой индекс - одна запись на документ - и мы решили, что можем жить без него.)


Индексы имеют большую стоимость памяти?
Матиас Ликкегор Лоренцен

@MathiasLykkegaardLorenzen Это зависит от количества уникальных значений для индексированного вами поля относительно оперативной памяти вашего сервера. В нашем случае createTimeиндекс был проблематичным, потому что он был уникальным для каждого отдельного документа, и эта коллекция была огромной. Индексирование других полей было в порядке, поскольку было меньше уникальных значений (значения были сгруппированы).
Joeytwiddle
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.