Как стать автором
Обновить

Комментарии 18

Была задача группировать по периодам (минута, час, день). Сделано было сначала через MapReduce MongoDB Docs Hierarchical Aggregation, а потом через Aggregate Framework, прирост в производительности порядка 70%.
Кстати, есть подозрение, что индекс или индекс + шардинг дадут ещё больший прирост запросов.
В нашем случае это уже были индексы + 5 шардов, на каждый 2 реплики. Map reduce отлично справляется с 20к записями, потом сложно идет, но странно то, что как раз когда записей много этот функционал востребован. А тот же Aggregate со всем справляется в лет, и перемалывает более 10000к документов за секунды
Можно узнать приблизительный размер шарда, структуру документов и индексов и сколько памяти кушает Монга под обработку 10М документов?
И ещё уточнение. Я так понимаю, в Монге вы храните некую статистику? Т.е. данные только вставляются и не обновляются?
Всё, что я читал/слышал об aggregation framework подтверждает это мнение. Но. Лично моё мнение, что они решают несколько разные задачи. Да, с помощью map-reduce можно решать задачи простейших группировок и конечно же он будет проигрывать инструментам, заточенным под эти задачи. Но когда речь идёт, скажем, о классической для map-reduce задаче анализа и классификации текстов, то тут уже в принципе не получится сравнивать map-reduce и aggregation, т.к. последний просто не справится с поставленной задачей. Т.е. сравнение изначально не совсем корректное. Под каждую задачу — свой инструмент.
Вы пробовали сравнить своё решение с аналогичной выборкой из постгреса?
К сожалению, нет. Я в курсе основных фишек Постгре и есть ощущение, что их массивы отлично решили бы эту задачу и без map-reduce.
mapReduce может применяться где угодно в программном коде, не обязательно в mongo.
Мне так кажется, что Ваш «документ описывающий отель» вполне соотносится с реляционной структурой.

«Дело в том, что при обработке большого количества отелей (скажем, 100 тысяч) и при условии, что каждый отель имеет наборы из суммарно 100 значений параметров, то будет произведено 10 миллионов вызовов функции emit()».
Тем то map-reduce и хорош, что им можно map'ать и reduce'ить не один раз а сколько угодно и что-угодно поэтапно. Т.е. в теории РБД Вам было бы совсем не обязательно map'ать все варианты, как в случае с mongo. Вы бы сначала map'нули подходящие подгруппы, за-reduce'или их, затем получили в подгруппах следующие характеристики по иерархии, map'нули их и т.д.
Т.е. не просто map-reduce, а древовидный map-reduce
Ещё один приятный момент — промежуточные результаты можно кэшировать.
Так и есть. В основной БД структура под хранение отелей насчитывает около 30 нормализованных таблиц. И конечно же есть ещё одно решение этой задачи, в котором строится темповая таблица с отелями после фильтрации и с помощью нескольких запросов с группировками можно получить тот же список сервисов с подсчётом количества отелей для построение фильтров. Но решение с Монгой показалось красивее и проще.

Иерархический map-reduce ИМХО хорош, когда нужна сложная аналитика по данным. В нашем случае — простейший подсчёт количества отелей в разрезе сервисов и других характеристик. Решается задача за один проход. Основной вопрос был в том, как именно мапить. При решении задачи в лоб, получалось слишком много значений на этапе маппинга. Оптимизированное решение упиралось в производительность интерпретатора JS в Монге. Как оказалось, серебряной пули не существует :)
Нельзя ли привести ТТХ комплекса, на котором работает Mongo, максимальный достигнутый RPS и примерное кол-во записей об отелях? И какая СУБД используется как основная?
Проект в разработке и поэтому ТТХ могу привести только сервера, на котором альфа развёрнута. Восьмиядерник, 24Гб оперативы и 2 ХДД в рэйде. Из которых Монга сейчас кушает что-то около 200Мб. Рабочих отелей до 5К. Тестировалось всё на 100К. Чистый RPS не замерял, так как считаю это сферическим конём в вакууме. Главный вопрос в том, сколько времени займёт полный цикл получения данных, на сколько активно при этом можно использовать кеш и какая алгоритмическая сложность решения, т.к. всё это добро придётся ещё не один год саппортить. По-факту, в данном случае резалтсет после фильтров по сервисам можно складывать в очень длинный кеш, т.к. данные по отелям редко меняются, а финальный резалтсет после фильтров по цене — в короткий кеш и миксовать их. Самое главное, что после всех этих махинаций страницы на разогретом кеше и с нагрузкой в 100 конкурентных соединений отдаются за 20-30мс и это полностью нас устраивает.

В перспективе будет 3-5 серверов с похожими конфигурациями, но по-большей части они будут использоваться для работы с ценовыми предложениями по отелям. Для начала — шардирование. По мере роста нагрузки будут докидываться слейвы.

Основная СУБД — MariaDb.
страницы на разогретом кеше и с нагрузкой в 100 конкурентных соединений отдаются за 20-30мс
Отличный результат.

Правильно ли я понимаю, что система заточена под запросы типа «Найди отель в Уганде, с шахматным клубом и поэтессами», а не на «найди мне double на 2 недели за $500 все равно где»?
Да, отправная точка поиска — страна. И дальше поиск идёт по любой из характеристик отеля плюс поиск по ценовым предложениям с разнообразными критериями.
Интересный опыт конечно, но после фильтрации отелей по стране и городу обычно остаётся не больше 10к строк (в подавляющем случае — до 500). А это можно легко обсчитать и за пределами БД.
Могу немного ошибаться, т.к. работаю в другом отделе, но на ostrovok.ru/ все отели города загружаются в браузер и фильтры затем прекрасно применяются прямо в браузере.

Ну и Array + GIN / GiST индекс в постгресе тоже можно использовать для случаев, когда у строки может быть несколько значений поля одновременно.
Отфильтровать — не проблема. Тут основное — это просчёт за один проход количества отелей, соответствующие каждому из сервисов. И таки да. Можно на С написать демона, который будет держать всю базу отелей с сервисами в памяти и делать обсчёт. :) Вопрос в целесообразности.

Островок — классный проект. Мы попроще :)
Ну я к тому, что демон на C не нужен, когда работаем с 500-1000 строк (количество отелей после фильтрации по стране и городу). Это можно спокойно посчитать и в application сервере (Python/PHP/Ruby etc) и даже непосредственно в браузере. Можно тем же мап-редьюсом на коленке, можно простой хеш-таблицей (псевдокод):
hotels = db.query("SELECT * FROM hotels WHERE country='russia' AND city='moscow';")  // вернет меньше 1000 строк
counter = {}
for (hotel in hotels) {
    for (amenity in hotel.amenities) {
        // здесь amenity это, например строка "wifi", "парковка", "бассейн" или соответствующий числовой id услуги
        counter[amenity] += 1
    }
}

И это возможно благодаря тому, что после фильтрации по стране и городу отсекается большая часть строк. А если нужно вывести все отели страны или вообще все отели, то конечно придется помучиться. Но, как правило, это никому не нужно.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории