Mongodb для банковских операций

3. Элементарные операции над данными. Установка заняла ровно 4 команды в консоли, подробнее об  Для доступа к монго используем консольную команду mongo: MongoDB shell version: 2.4.9 connecting to: test Welcome to the MongoDB shell.

Видеозапись выступления
Сергей Туленцев: Здравствуйте, меня зовут Сергей Туленцев. Сегодня я расскажу вам про MongoDB. MongoDB – это хорошая база данных. Многие ее любят, используют везде, где только можно, и даже иногда там, где нельзя. Попробую сегодня рассказать, почему вы можете не захотеть использовать ее для своего следующего проекта. Сразу предупреждаю, что причин, на самом деле, не очень много, и не все они серьезные.
MapReduce
Начнем с MapReduce. Как известно, MongoDB относится к классу так называемых NoSQL-решений. Соответственно, в нем нет SQL и соответствующих возможностей по обработке данных. Есть свой язык запросов с какими-то возможностями. Если этих возможностей не хватает, предлагается использовать MapReduce.
Вообще это мощный механизм, многие его используют. Но конкретно в этой реализации есть два фатальных недостатка. Первый – он медленный, очень медленный. Второй – он однопоточный.
Оба эти недостатка обусловлены тем, что функции мы пишем на JavaScript, и выполняется это тоже внутри встроенного JavaScript-движка. Термин «однопоточный» в данном случае означает то, что в каждый конкретный момент исполняется только один экземпляр JavaScript-кода. Но экземпляров, запущенных MapReduce, может быть несколько. Чтобы понять, почему это медленно и не очень подходит для RealTime-запросов, подробнее рассмотрим алгоритм работы.
Маппинг
Сначала у нас идет стадия маппинга, мы читаем входную коллекцию. Для этого мы берем блокировку чтения (англ. read lock) и отпускаем каждые 100 документов для того, чтобы дать другим операциям возможность выполниться.
Далее, как мы считали эту пачку документов, мы для каждого документа выполняем "map". Для этого мы берем блокировку JavaScript и преобразуем документ из Bison в JSON. Если функция "map" у нас выполнила лимит, и нам нужно записать что-то во временную коллекцию, мы пишем это во временную коллекцию. Для этого мы берем блокировку записи (англ. write lock).
Стадия "Reduce"
Итак, когда-нибудь это закончится, и наступит стадия "Reduce". Здесь мы делаем то же самое, только берем временную коллекцию в качестве входной и функцию "reduce" как функцию, которую надо выполнить над документом. Однако результаты, которые возвратит эта функция, мы не записываем никуда. Мы пока накапливаем их в памяти.
Потом наступает момент пост-обработки, и мы их все разом записываем в коллекцию, которую мы указали как выходную. Для этого мы берем блокировку записи и записываем все "пачкой", а не каждый документ по отдельности. Все эти мелкие блокировки и JavaScript, а также преобразование из Bison в JSON не очень положительно влияют на быстродействие.

Примеры на mongo shell или на python. Миграция в на новые версии в mongodb.  Как правило все эти модификаторы используются для операций обновления в db.test.update() и db.test.findAndModify().

Я это тестировал на таблице в миллион документов. Одни и те же результаты посчитал встроенным языком, встроенными средствами и MapReduce. MapReduce был в 4 раза медленнее при условии, что это был единственный запрос в системе, а другие запросы не выполнялись.
Memory Mapped Files: плюсы и минусы
Memory Mapped Files – это механизм современных операционных систем, который позволяет установить прямое соответствие между файлом на диске и областью памяти. После этого приложение может работать напрямую с памятью, читать и записывать в нее. Система позаботится о сбросе изменившихся участков на диск. В приложении в результате увеличивается быстродействие, потому что доступ к памяти на порядки быстрее, чем системный вызов.
Но есть и недостатки. Недостаток ровно тот же – управление памятью на системе. Мы не контролируем, какие страницы у нас находятся в памяти, а какие вытеснены на диск.
Конкретно в MongoDB очень важно, чтобы в памяти были хотя бы индексы, которые мы используем при запросах. Если индексов в памяти нет, будет очень плохо.
Допустим, что у нас есть система, она как-то работает. Кто-то запускает массивный MapReduce на большой коллекции, и он начинает обрабатывать эту коллекцию, "поднимать" все в память, вытесняя индексы. Уже сама эта операция будет небыстро работать, потому что, в основном, происходит чтение с диска. Остальные запросы у нас тоже начнут тормозить. Система может очень долго восстанавливаться после такого запроса.
Если бы мы управляли памятью сами, то теоретически мы могли бы лучше знать, какие области у нас «горячие», какие не надо выгружать или, например, ограничить размер буфера для таких операций полного сканирования.
Следующий недостаток – это «дыры» в файлах. Когда мы удаляем какой-то документ, он, на самом деле, конечно, физически не удаляется. Он помечается как «удаленный» и будет совсем удален при следующей дефрагментации коллекции. Но до тех пор он будет лежать на диске и занимать место.
Представим себе такой экстремальный случай, что мы удалили большинство документов, но равномерно (4 из каждых пяти). Мы так это дело и оставили, пытаемся посчитать какую-то агрегатную статистику. В результате, чтобы загрузить 5 документов, нам потребуется загрузить в 5 раз больше страниц. Если раньше они умещались, скажем, на одной странице, то теперь на каждой странице будет лежать один реальный документ и 4 фантомных. Это лишняя нагрузка на диск и расход памяти. Конечно, после таких массивных операций надо делать «контакт».

Глава 2 — Обновление. В первой главе мы изучили три из четырёх операций CRUD (create, read, update and delete).  Во-первых, в отличие от SQL-команды UPDATE, в MongoDB update заменяет документ целиком. Из-за этого модификатор $set очень

Во второй версии MongoDB, которая вышла недели 2-3 назад, появилась команда «контакт», которая позволяет дефрагментировать отдельную коллекцию без необходимости делать это для всей базы данных. Но такая операция по-прежнему блокирует все остальные операции, поэтому лучше производить ее на ведомом сервере (англ. slave).
Еще один недостаток, связанный с памятью, напрямую не связан с технологией MMF. В MongoDB нельзя ограничить размер используемой памяти. Специалисты хостинговой компании "Selectel" утверждают, что MondoDB читает объем доступной оперативки при старте и не проверяет потом это значение. Соответственно, это создает проблемы при запуске MongoDB в "облачной" среде, где объем доступной оперативки может как повышаться, так и понижаться.
Мне захотелось проверить эту информацию. Я задал этот вопрос на официальном форуме, но никто из "10gen" мне не ответил. Поэтому предлагаю поверить ребятам из "Selectel".
Одна из самых больших проблем MondoDB – это блокировки и гранулярность блокировок. В моем личном списке недостатков у них вообще первое место. В MondoDB принята модель «1 писатель и много читателей». Причем «писатель» один не на документ или коллекцию, как во многих других БД, и даже не на базу данных. Он один на сервер. Сколько бы баз данных у нас на сервере ни было, только в одну коллекцию в данный момент может прийти запись.
Причем этот «писатель» блокирует всех остальных (других и «писателей», и «читателей»), и одно неудачное массовое обновление может положить всю систему. Вся система страдала от этого до версии 2.0, в которой разработчики научились ненадолго отпускать блокировку записи при массовых обновлениях и, таким образом, давать шанс другим операциям выполниться. Как это работает на деле, я, честно говоря, не проверял. Но они обещают, что это будет работать нормально.
Сюда же, к блокировке относим блокировки с миграцией чанков (англ. chunk). Когда задействован кластер, шарды и все такое. Вкратце расскажу про процесс миграции чанков. Когда надо перенести часть данных на какой-то новый шард, выбирать этот новый шард (наименее загруженный по умолчанию), данные туда копируются, происходит передача прав этому новому шарду путем записи в файл конфигурации сервера, и данные удаляются со старого шарда.
Так вот, в версии 1.6 жутко тормозило это удаление. Оно могло висеть часа 3 и блокировало все остальные операции. Я достал всех разработчиков по этому поводу. Они что-то там подкрутили, и перестало тормозить.
Потом вышла версия 1.8, в которой это перестало тормозить. Но миграции продолжали зависать. Выяснилось, что во время миграции чанков происходит вызов некоей процедуры setShardVersion, которая, видимо, изменяет что-то в файлах конфигурации на серверах. Она вызывается несколько раз за миграцию. Она тоже повисала очень легко минут на 20. Иногда отрабатывает, иногда повисает. Устранялось это перезапуском сервера, потому что ждать, пока отвиснет, очень не хотелось.
Запросы и оптимизация
В настоящее время MongoDB может использовать только один индекс при исполнении запросов, даже если всего этих индексов несколько. Соответственно, это отметает всякие продвинутые оптимизации вроде слияния индексов (англ. index merge). При этом индекс не всегда угадывается правильно с точки зрения разработчика. Разработчик думает, что должен использовать один индекс, а используется другой. Почему? Потому что MongoDB выбирает этот план эмпирически.
В первый раз запускаются все доступные планы запросов, и MongoDB смотрит, какой выполнится первым. Этот MongoDB считает оптимальным и будет в дальнейшем использовать его. Через некоторое время MongoDB повторяет эту попытку. Снова запускается план, MongoDB смотрит, какой выполнится быстрее, потому что данные в коллекции могут измениться таким образом, что предыдущий план уже не является оптимальным.
Шардинг
В MongoDB, как во многих других системах, шарды равноправны. Данные на них распределяются равномерно. Однажды, еще будучи излишне оптимистичным по поводу возможностей MongoDB, я добавил новый шард в кластер. Все бы было ничего, но этот шард был в 3 раза меньше текущих машин, которые у меня были. Шард добавился, данные начали раскидываться, я пошел спать. Утром проснулся – данные перераспределились, и все тормозит. Работает, но тормозит, очень тяжело отвечает. На этот шард распределилась треть данных. Было два шарда, я добавил еще один, соответственно, везде стало по трети. Я наивно думал, что на него положится одна седьмая. Но нет, все начало тормозить, и тормозило еще два дня, пока я изымал этот шард из кластера.
Ошибка, конечно, была моя. Но, кажется, было бы неплохо уметь задавать вес шарда, потому что не всегда удается достать одинаковые машины. В файле конфигурации задавать вес: этот может хранить X данных, этот – 2X, этот – 0,3X. Тикет насчет этого я отправил, но пока ничего непонятно.
Дальше идет объективный недостаток: нет распределения коллекций. Когда мы в кластере Mongo

Удивительно, но MongoDB подходит под все эти определения. Как документ-ориентированная СУБД, Mongo — это  Глава 2 — Обновление. В первой главе мы изучили три из четырёх операций CRUD (create, read, update and delete).

mongod_user=»mongodb» — от какого пользователя будет работать mongod; также дополнительно можно прописать mongod_flags  работа с текущими операциями: db.currentOp() — показать в текущей БД; db.killOp() — убить текущую операцию в базе

Genghis — довольно милая, но простенькая web-based админка для выполнения простых CRUD-операций с Mongo.  Автор Genghis вдохновлялся MongoHub, довольно известной админкой MongoDB для OS X, и старался сделать ее такой же