Подтвердить что ты не робот

Производительность агрегации MongoDB

У нас есть проблема запросов агрегации, работающих длительное время (пару минут).

Коллекция:

У нас есть коллекция из 250 миллионов документов с примерно 20 полями на документ, Общий размер коллекции составляет 110 ГБ.

У нас есть индексы над полями "our_id" и dtKey.

Оборудование:

Память:

ОЗУ 24 ГБ (6 * 4 ГБ DIMMS 1333 МГц)

диск:

Lvm 11TB, построенный из 4 дисков 3TB дисков:

  • Максимальные мгновенные передачи данных 600 МБ/с.

  • Шпиндель 7200 об/мин. Средняя латентность = 4.16 мс

  • RAID 0

CPU:

2 * E5-2420 0 @1,90 ГГц Всего 12 ядер с 24 потоками. Dell R420.

Проблема: Мы пытаемся сделать запрос агрегирования следующего:

db.our_collection.aggregate(
    [
        {
            "$match":
            {
                "$and":
                    [
                        {"dtKey":{"$gte":20140916}},
                        {"dtKey":{"$lt":20141217}},
                        {"our_id":"111111111"}
                    ]
            }
        },
        {
            "$project":
            {
                "field1":1,
                "date":1
            }
        },
        {
            "$group":
            {
                "_id":
                {
                    "day":{"$dayOfYear":"$date"},
                    "year":{"$year":"$date"}
                },
                "field1":{"$sum":"$field1"}
            }
        }
    ]
);

Этот запрос занимает пару минут для запуска, когда он выполняется, мы можем видеть следующее:

  • Текущая операция Mongo дает более 300 тысяч раз
  • На iostat мы видим ~ 100% использование диска

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

После запуска для 3 - 4 пользователей кажется, что первый из них уже выгружен из кэша, и запрос занимает много времени.

Мы проверили счет на соответствующую часть и увидели, что у нас есть пользователи 50K-документов, а также пользователи с документами 500K,

Мы попытались получить только соответствующую часть:

db.pub_stats.aggregate(
    [
        {
            "$match":
            {
                "$and":
                    [
                        {"dtKey":{"$gte":20140916}},
                        {"dtKey":{"$lt":20141217}},
                        {" our_id ":"112162107"}
                    ]
            }
        }
    ]
);

И запросы, кажется, занимают приблизительно 300-500 М памяти,

Но после запуска полного запроса он, кажется, занимает 3.5 ГБ памяти.

Вопросы:

  • Почему конвейерная обработка агрегации занимает столько памяти?
  • Как мы можем увеличить нашу производительность, чтобы он работал в разумные сроки для HTTP-запроса?
4b9b3361

Ответ 1

  • Почему конвейерная обработка агрегации занимает столько памяти?

Просто выполнить $match не нужно будет читать фактические данные, это можно сделать по индексам. Через доступ к проекции field1, фактический документ должен быть прочитан, и он, вероятно, также будет кэшироваться.

Кроме того, группирование может быть дорогостоящим. Как правило, он должен сообщать об ошибке, если на этапе группировки требуется более 100 МБ памяти - какую версию вы используете? Это требует сканирования всего набора результатов перед уступкой, и MongoDB должен будет хотя бы хранить указатель или индекс каждого элемента в группах. Я полагаю, что основной причиной увеличения памяти является первая.

  1. Как мы можем увеличить нашу производительность, чтобы он работал в разумные сроки для HTTP-запроса?

Кажется, что ваш dtKey кодирует время, и группировка также выполняется в зависимости от времени. Я попытаюсь воспользоваться этим фактом - например, путем предкоммутации агрегатов для каждого дня и комбинации our_id - имеет большой смысл, если нет никаких критериев, и данные больше не меняются.

В противном случае я попытаюсь перенести критерий {"our_id":"111111111"} в первую позицию, потому что равенство должно всегда предшествовать запросам диапазона. Я предполагаю, что оптимизатор запросов в структуре агрегации достаточно умен, но стоит попробовать. Кроме того, вы можете попытаться превратить ваши два индекса в один составной индекс { our_id, dtkey }. Переходы индексов теперь поддерживаются, но я не уверен, насколько это эффективно. Используйте встроенный профиль и .explain() для анализа вашего запроса.

Наконец, MongoDB предназначен для многопользовательской записи и сканирования наборов данных сотен ГБ с диска в миллисекундах, не представляется возможным вообще вычислить. Если ваш набор данных больше, чем ваша оперативная память, вы столкнетесь с большими задержками ввода-вывода в масштабе десятков миллисекунд и выше, десятки или сотни тысяч раз из-за всех необходимых операций с дисками. Помните, что при случайном доступе вы никогда не приблизитесь к теоретическим последовательным скоростям передачи диска. Если вы не можете прекомпетровать, я думаю, вам понадобится намного больше ОЗУ. Может быть, SSD-диски помогут, но это все догадки.