Вынесено из комментов к посту
vitus_wagner’а про pump.io.
Из опыта работы с MongoDB получается, что её писали вредители. Абсолютно весь API и дизайн заточен под то, чтобы как можно сильнее оградить клиентский код от возможности делать полезные запросы к базе и увеличить вероятность непреднамеренного уничтожения данных.
Типичный симптом при использовании Mongo — приложение работает, пока объём или количество документов в базе не превышает некоторого эпсилона, достаточно большого, чтобы проблемы не возникали в тестировании.
Во-первых, Mongo — блокировочник. То есть, когда в базу идёт активная запись, все читатели сидят и ждут, пока писатель соизволит отпустить блокировку.
Во-вторых, Mongo очень неохотно освобождает дисковое пространство. То есть логически удаляемые записи физически продолжают занимать место, до ближайшей компактизации. Компактизация требует, чтобы на файловой системе было свободно хотя бы ещё столько же места, сколько база уже занимает. То есть, компактизация базы, уже сожравшей всё свободное место, штатными средствами невозможна — туши сервер, снимай бэкап по сети, дропай файлы базы, перезаливай с бэкапа.
Иногда делу можно помочь ограниченными (capped) коллекциями. Это такая коллекция, которая растёт только до определённого предела. Ограничение устанавливается на общий размер данных в коллекции. Не на количество документов, не на возраст самого старого документа — на размер. Чисто техническая реализация, без какой-либо оглядки на предметную область. Далее, реализованы они так, что документы можно модифицировать, но только в сторону уменьшения размера. Удалять документы из capped коллекций нельзя, только дождаться, пока они будут удалены автоматически (при вставке очередного документа). Или можно очистить всю коллекцию. Короче говоря, capped коллекции очень ограничены в функциональности по сравнению с обычными.
В-третьих, документы Mongo ограничены в объёме неким искусственным лимитом в 16 мегабайт. Это бы было ничего для конечных документов, хранимых в базе.
Однако, иногда с базы хочется посчитать какую-нибудь развесистую агрегацию — то, что в SQL делается однострочником вида group by или right join. Для этого есть встроенные агрегирующие функции. Которые как бы работают, но их результат является документом. Со всеми вытекающими последствиями, см. п. 3.
Поэтому для всех интересных агрегаций приходится использовать механизм map/reduce. Для того, чтобы начать мочь переформулировать запросы вида group by и right join, нужно вывернуть мозг в чисто-функциональную парадигму. Писать в базу из map/reduce нельзя, там разрешены только чистые функции. Нет, есть одна лазейка: функциям, используемым в map/reduce, разрешается использовать объект контекста (так называемый scope). Объект scope является документом. Все промежуточные результаты map и reduce являются документами. См. п. 3. Если потребовать результат map/reduce напрямую (как возвращаемое значение), то он тоже должен укладываться в лимит.
Но можно делать map/reduce в коллекцию, в этом случае ограничение на весь результат снимается. Ограничения на все промежуточные документы остаются.
Часть ограничений на промежуточные результаты удаётся обойти, если делать map/reduce не по всем данным, а частями, или регулярно с переиспользованием уже насчитанных данных. Это называется инкрементальным map/reduce’ом. По умолчанию инкрементальный map/reduce перетирает прошлые насчитанные данные новыми, а не объединяет. (Это к заявлению о непреднамеренной потере данных.)
В-четвёртых, запросы на обновление (модификацию) документов. У них есть один аргумент — критерий поиска, и другой — как модифицируем. И третий — флаги/опции. По умолчанию, Mongo найдёт один документ, удовлетворяющий критерию, заранее неизвестно какой. Это gotcha, намеренное[citation needed] отличие от семантики SQL. Чтобы модифицировать все соответствующие документы, нужно явно указать флаг. Далее, если параметр, указывающий изменения, не содержит специальных операторов, то целевой (обновляемый) документ просто будет перезаписан этим. (В отличие от SQL, где указанные в запросе поля будут обновлены, а остальные — оставлены без изменения.) Это всё тоже очень помогает непреднамеренно привести базу в неизвестное состояние, из которого в общем случае нельзя ни вернуться в исходное, ни получить желаемое. Транзакции с возможностью rollback’а? Не, не слышал.
В-пятых, Mongo нестойка к аварийному выключению. При внезапном прекращении процесса файлы базы могут остаться в неконсистентном состоянии, в котором сервис отказывается запускаться и требует ручного вмешательства. Совсем как MySQL/MyISAM. Нет, транзакционная/журналируемая файловая система не поможет.
И в-шестых, Mongo очень быстро принимает записываемые в неё документы (и этот факт способствует тому, чтобы её выбирали в качестве хранилища в новых проектах), но очень неохотно отдаёт их обратно. Бэкап данных из Mongo или полная выборка для целей обработки внешними средствами подчиняется всем законам блокировочников.
no subject
Date: 2014-01-08 05:22 (UTC)этот эпсилон примерно равен количеству индексов + горячих данных и он же примерно равен объему ОЗУ. Как только начинается активная работа с дисками, все хужеет на глазах.
В принципе, MySQL работает так же. Но если при дальнейшем повышении нагрузки MySQL кряхтит, но тянет (время выполнения запросов растет почти линейно - по моим ощущениям), то MongoDB просто умирает - у нее забивается очередь выполнения, а текущие запросы как будто блокируют друг друга. Иногда вообще возникают забавные ситуации - диски временно протормозили из-за фоновых процессов, MongoDB встала колом и стоит. Убили все запросы в очереди - "отряхнулась и дальше пошла", как ни в чем не бывало.
Проблема в том, что объем индексов можно оценить, а объем горячих данных - нет.
Из-за приоритета записи возникает еще одна особенность: пока все хорошо (индексы и горячие данные лежат в памяти) - MongoDB, фактически, только пишет на диск. Но как только производительности дисков перестает хватать, MongoDB опять встает колом.
Я тоже вздрагиваю при слове "Mongo". ^_^
no subject
Date: 2014-01-08 07:05 (UTC)Про вмещаемость горячих данных в память — это полезная мысль. Я вполне верю, что при переходе через этот порог возможно качественное изменение производительностных характеристик.
Но вообще я имел в виду тот фазовый переход, который наступает, когда нужные в работе агрегации перестают вмещаться в лимит размера документа. И ещё когда кончается свободное место.
Опять же, при попытке считать агрегации или сбэкапить всю базу внезапно все данные становятся не то чтоб горячими, но слегка тёплыми.
no subject
Date: 2014-06-15 19:25 (UTC)no subject
Date: 2014-06-16 01:13 (UTC)У каждого же свои критерии "лучшести", они еще и меняются со временем.
no subject
Date: 2014-01-08 07:46 (UTC)no subject
Date: 2014-01-08 08:35 (UTC)Как админ хостингового сервера , буду вручать Ваш текст клиентам в контексте вопроса, "почему бы нам не использовать Mongo вместо MySQL?"
Очень выразительно.
no subject
Date: 2014-01-08 10:05 (UTC)no subject
Date: 2014-01-08 10:10 (UTC)У клиентов бывает иллюзия (особенно подкреплённая фанатиками-разработчиками), что смена платформы решит какую-то из его проблем И НЕ ПРИНЕСЕТ НОВЫХ. :)
no subject
Date: 2014-01-08 17:15 (UTC)no subject
Date: 2014-01-09 02:05 (UTC)Ну, в Идеальном Мире, по идее, сервер должен иметь достаточно толстый/умный UPS, чтобы при внезапном выключении электричества ссигналить тревогу и протянуть пять минут, пока всё аккуратно укладывается.
Но от внезапного
kill -9из-за разбушевавшихся соседей по виртуализации — не спасёт.no subject
Date: 2014-01-09 04:02 (UTC)no subject
Date: 2014-06-15 12:07 (UTC)no subject
Date: 2014-06-15 17:59 (UTC)Начиная с 2.6 – курсором.
no subject
Date: 2014-06-16 04:20 (UTC)No title
Date: 2014-06-15 11:38 (UTC)no subject
Date: 2014-06-15 12:06 (UTC)остальное можно не читать.
За что мы не любим MongoDB
Date: 2014-06-15 12:18 (UTC)no subject
Date: 2014-06-15 15:41 (UTC)У MongoDB разве есть компактизация? Я считал, что он измененные данные просто меняет по месту на диске.
Со включенным журналированием он вроде как должен быть устойчив к внезапным выключениям питания. Это не так? Без журналирования согласен, взрывается.
Это не наезд, а просто хочу прояснить для себя ситуацию.
no subject
Date: 2014-06-15 16:08 (UTC)Изменяемый документ может быть перезаписан по месту, если новая версия размером не превышает старой. Иначе же старая версия по старому месту будет удалена, а новая записана в конец коллекции, а старое освободившееся место не переиспользуется.
Компактизация просто создаёт новые файлы для коллекции, копирует в них все неудалённые документы, затем переименовывает новые файлы поверх старых.
Журналирование появилось относительно недавно и, вероятно, в каких-то из тех опытов, от которых у меня остались впечатления, его просто не было или оно было по умолчанию отключено.
no subject
Date: 2014-06-15 16:23 (UTC)Журналирование появилось в версии 1.8, это было конец 2011 или начало 2012. Слышал, что после этого база уже так не взрывается, но проверить самому не довелось.
Кстати, дополню ваш список: у MongoDB довольно криво и геморно реализован шардинг.
Чтобы размазать данные на 4 шарда, обеспечив при этом фактор репликации 3, нам потребуется 12 серверов, которые придется собирать в двухуровневое дерево вручную.
no subject
Date: 2014-06-15 16:24 (UTC)У этого журналирования не работает автоочистка журнала. ^_^
В доке написано, что после начала нового файла, старый должен на автомате стираться. На версии 2.4.7 это не так - тупо файлы не удаляются, приходится самим по крону стирать.
no subject
Date: 2014-06-15 16:30 (UTC)С включенным журналированием mongodb умеет определять незавершенные операции и автоматически и сохраняет в отдельном каталоге. Теоретически, это может чему-то помочь.
На практике, роль мастера (PRIMARY) в репликасете уже полчаса как перешла на другой сервер и что делать с этими старыми запросами - непонятно. Данные могли быть перезаписаны, а как это определить? Только ручной разбор всего оплога на новом мастере, это "весело".
no subject
Date: 2014-06-15 16:38 (UTC)no subject
Date: 2014-06-15 18:36 (UTC)no subject
Date: 2014-06-16 01:13 (UTC)no subject
Date: 2014-06-20 19:56 (UTC)* монга нормально работает на десятках терабайт.
* проблема не в блокировках, а в i/o, так же блокировки есть и в mysql, просто о них не говорят. главное что монга производительнее.
> штатными средствами невозможна — туши сервер, снимай бэкап по сети
за памятью нужно следить, а вообще "дефрагментация" делается легко, достаточно удалить файлы, после чего монга сама вытягивает данные из реплики и нода "включается".
> лимитом в 16 мегабайт
если вам не хватает, то у вас что-то не то с архитектурой (для больших бинарников есть gridfs)
> group by или right join
тут он не нужен, как вы будете join-нить если таблица размазана по 20 серверам? + это медленно.
это вам не sql, для каждой задачи свой инструмент, если вам в проекте нужны join-ы, то вам никто не запрещает подключить (my)sql для этой задачи.
в больших проектах обычно используется куча технологий, "facebook же не на одном mysql работает..."
> map/reduce
похоже вы не понимаете для чего он нужен
> Транзакции с возможностью rollback’а? Не, не слышал.
достаточно атомарных операций, так же можно использовать транзакционный сервер где это необходимо. транзакций нет потому что - это тормоз, особенно в шардинге, тут нужно включать мозг.
и опять же если они позарез нужны, то подключите (my)sql.
5) По стабильности рекомендуемая конфигурация mongodb ещё фору даст mysql.
6) С отдачей проблем нет, если скорости не хватает - подключите серверов.
бекапы принято снимать с репликационной копии.
no subject
Date: 2014-06-21 07:08 (UTC)«Блокировочник» и «версионник» — это два термина как раз из области реляционных баз данных. И да, MySQL — тоже блокировочник. Разумеется, из MongoDB и MySQL «оба хуже».
Это предполагает наличие репликасета. То есть не менее чем трёх узлов там, где типичной SQL-базе требуется один.
Отлично понимаю. Map/Reduce, в функциональном программировании, это механизм, позволяющий, имея коллекцию элементов, сначала посчитать некую функцию от каждого элемента независимо (map), а потом сагрегировать результаты этой функции в одно результирующее значение.
У нас была основная задача сбора данных, где каждое событие — это один документ. Событий идёт очень много и постоянно и поэтому их нужно как можно быстрее записывать.
Далее, очень изредка, по собранным данным нужно делать некие агрегации. К примеру, сгруппировать события в цепочки с одинаковым user_id, упорядоченные по дате; выделить те цепочки, где последнее событие имеет тип «неудачная установка», далее снова сгруппировать их по типу устройства и упорядочить по количеству различных user_id. Это решалось бы в две операции map/reduce, где первая редукция собирает цепочки, а вторая даёт статистику. Но, блин, цепочки не влазят в 16M. Нет, собирать вместо всей цепочки только последнее событие не хочется, потому что интересно посмотреть на всю последовательность событий.
В моём представлении, никакие заботы о скорости не оправдывают потери корректности. На ненагруженной базе мне нужны транзакции, чтобы при работе руками с базой я мог начать транзакцию, выполнить модифицирующий запрос, посмотреть на его результаты, сказать
«ой, б$#»rollbackи попробовать заново. В отсутствие транзакций я должен сначала забэкапить данные, затем пробовать модификации, восстанавливая базу с бэкапа после каждой неправильной попытки. А она будет по меньшей мере одна, потому что я не ожидаю, что на запрос «в коллекции [x] где p(x) заменить на f(x)» замена остановится после первого срабатывания.На нагруженной базе пусть тормозит, пока человек осознанно не примет решение об отключении транзакционной безопасности.
Это предполагает наличие репликасета. Ну так если базовые вещи работают только в репликасете, то пусть в описании продукта будет написано «вы в принципе можете работать без репликации при разработке, но на продакшене репликация крайне рекомендуется».
no subject
Date: 2014-06-21 09:07 (UTC)> восстанавливая базу с бэкапа после каждой неправильной попытки
вот пример (http://cookbook.mongodb.org/patterns/perform-two-phase-commits/) коммита, как можно провести операцию с возможностью rollback, причем операции с одним "счетом" могут проводиться в одно и тоже время, когда с транзакциями будет "очередь". это не sql, тут нужны другие подходы.
И как я выше уже отметил, не надо пытаться сделать все на одной технологии, например у меня были проекты где Mongodb и mysql вместе. даже для простого сайта используется 3-10 (и более) разных технологий (веб-сервер, бд, python/php...), что страшного подключить ещё одну?. Правда для мизерных "пет-проджектов" оно не нужно, там можно и в файлах хранить.
no subject
Date: 2014-06-23 20:48 (UTC)Так что нет ничего удивительного, что монга неявно апедполагает наличие реплика-сета и отсутствие транзакционности.
Но это не опрадывает
> а вообще "дефрагментация" делается легко, достаточно удалить файлы, после чего монга сама вытягивает данные из реплики и нода "включается".
no subject
Date: 2014-06-23 20:34 (UTC)