LINUX.ORG.RU

Вопросы по MongoDB

 


0

4

Здравствуйте!
С Монгой не работал, возник ряд вопросов:

1. Пишут, что лимит на коллекцию - 16MB. Много ли это? Пока что нет четкого понимания, что такое Коллекция.
Допустим, в коде (Perl):

my $conn = MongoDB::Connection->new; # connection
my $db = $conn->test;  # use db test
my $users = $db->users; # collection?
Правильно ли я понимаю, что users - это и будет коллекция (аналог таблицы) ?
16 МБ - как-то маловато, не?

2. Чтобы обойти это - нужно юзать GridFS, так? Какие ограничения и подводные камни? Я так понимаю, что GridFS не является полностью прозрачной (т.е. не получится просто прикрутить ее и не вносить изменений в код)?

3. Пишут, что сейчас блокировка там на уровне БД. Правильно ли я понимаю, что если 5 процессов пытаются одновременно писать в одну БД (test из примера), то сможет это сделать только 1 ?
Что будут делать остальные - ждать, пока бд не разлочится или просто отвалятся? В данном случае, лучше использовать синхронную запись, а не асинхронную, которая по умолчанию, так?

★★★★★
Ответ на: комментарий от VladimirMalyk

А как все-таки лучше хранить комментарии к сообщению, например?
Если использовать ссылки, то, получается, в объекте post должно быть поле comments, в котором хранится _id другого Документа - в котором хранится список комментариев. Выходит, мы для каждого поста ВСЕ комментарии сохраняем в одном документе - и можем превысить лимит в 16МБ.

А если просто создать коллекцию комментариев где каждый документ - это отдельный комментарий, у которого есть поле post_id ? Чем такой вариант хуже? Медленно будет работать?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Но все равно, хотелось бы разобраться, почему не создавался индекс.

Не уверен, что unique => 1 подразумевает тоже самое, что и такая запись:

$coll->ensure_index({ id => 1 }, { unique => boolean::true });

Подробнее: http://search.cpan.org/perldoc?MongoDB::Collection

Вообще в таких случаях рекомендую читать ошибки драйвера: через вызов last_error или опцию safe, которая вызовет croak в случае ошибки:

eval {
  $coll->ensure_index({ id => 1 }, { unique => boolean::true, safe => 1 });
};
warn $@ if $@;

-- gh0stwizard

anonymous
()
Ответ на: комментарий от VladimirMalyk

Ну, использовать другую базу уже не получится. Как это лучше сделать в монге? Ссылками или тупо отдельными документами с id'шниками постов?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

А как все-таки лучше хранить комментарии к сообщению, например?

Тут зависит от проектирования приложения. Встроенные документы (embed) быстры, но их сложнее модифицировать. Более того, чем больше документ, тем более дорогой будет операция обновления, т.е. io-нагрузка возрастет при частых обновлениях. Придется также придумать как избежать переполнения документа (например, спам, строгое проектирование, что комментариев будет не более 1000 и т.п).

Вложенные документы через ссылки т.е. через _id менее удобны, однако сокращают размер документа. Замечу, что простая сортировка комментариев по времени уже может стать проблемой. Если приложение будет вычислять из каждого монговского objectId время будет оверхед как по cpu, так и по времени сортировки, если таких сообщений будет тысячи.

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

А если просто создать коллекцию комментариев где каждый документ - это отдельный комментарий, у которого есть поле post_id ? Чем такой вариант хуже? Медленно будет работать?

Усложнится лишь часть приложения. Однако, представь ситуацию, где ты можешь вывести комментарии через ajax по 50 штук, а шапка остается висеть. Тогда, легко можно использовать limit(), skip() заместо splice. Более того, такая коллекция может иметь свой shard-key, который будет учитывать совершенно иные критерии по разбросу по серверам. По собственному опыту работа с массивами/списками менее удобна, чем в контексте отдельного документа. Минус разбивки по коллекциям является меньшая пропускная производительность в случае Perl без использования kernel threads через fork, pthreads (use threads). Не беру в расчет Coro, т.к. на локалхосте от него пользы не шибко много. Хотя на форуме mongo кто-то использует перл + use threads в CGI приложениях и находили там несколько багов. Смотри сам: при расчете твоего приложения на миллион пользователей придется писать на сях или чем-то другом, где используются нормальная многопоточность.

http://www.mongodb.org/display/DOCS/Production Deployments

-- gh0stwizard

anonymous
()
Ответ на: комментарий от kovrik

Ну, ничего страшного, что я его сам задаю, да?

Монга гарантирует уникальность _id в кластере.

Если вы пытаетесь туда зафигачить инты с автоинкрементам - наверное лучше на реляционку вернуться.

Ну если вам там надо свои дополнительные ID, например для красивых URL - заведите еще одно поле. Потом будет не так больно.

Vit ★★★★★
()
Ответ на: комментарий от VladimirMalyk

у меня нет историй успеха по ручному впихиванию _id

Создатели Mongo утверждают, что это обычный индекс, его значения должны быть уникальны с учетом типа значения (скорее всего 1.0 (float) != 1 (int)).

x_hash
()
Ответ на: комментарий от kovrik

И все равно вставляются объекты с одинаковым id. В чем может быть проблема?

Посмотри из консоли для нужной базы

use <db_name>
db.<collection_name>.getIndexes()

Там будет указано, по каким ключам индекс и уникальный ли он.

x_hash
()
Ответ на: комментарий от x_hash

Проверил, 1.0 float и 1 int в уникальный индекс (_id) не вставить.

x_hash
()
Ответ на: комментарий от kovrik

А как все-таки лучше хранить комментарии к сообщению, например?

Зависит от того, какие будут запросы.

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

x_hash
()
Ответ на: комментарий от x_hash

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

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

Я же скорее говорю об атомарности изменений - изменения в документе (были) атомарны, т.е. если из одного процесса мы меняем одним запросом два поля, а в другом к этим полям обращаемся, во втором процессе мы не получим данных в их промежуточном значении (одно поле уже измененное, а второе еще нет).

yoghurt ★★★★★
()

Правильно ли я понимаю, что если 5 процессов пытаются одновременно писать в одну БД (test из примера), то сможет это сделать только 1 ?

В единицу времени - да. Тем не менее попасть в дедлок как в реалиционках невозможно. Более того блокировка в монго умная, все блокировки на запись переодически прерываются запросами на чтение. Одновременно писать в несколько бд, коллекций ты можешь спокойно, просто монго будет переключаться между запросами. Единственный случай, когда монго отвалится - две записи с одним уникальным индексом (индекс должен быть построен). Индексы можно строить в любое время, при большом количестве записей (несколько миллионов) лучше после. Аналогично при построении индекса с дубликатами по уникальным индексам - база руганется.

Что будут делать остальные - ждать, пока бд не разлочится или просто отвалятся? В данном случае, лучше использовать синхронную запись, а не асинхронную, которая по умолчанию, так?

Ждать, ожидание исчисляется в миллисекундах. По дефолту в журнал пишутся все запросы с ожиданием более 100 мс. Это поведение настраиваемо. Нагнуть бд невозможно практически - скорее упрешься в производительность дисков (см. утилиту mongoperf), либо в производительность своего приложения (не хватит cpu). Сама монго многопоточная, при этом на все клиентские соединения выделяется вроде только один поток. В перле через mongodb::async удавалось одним процессом нагрузить монго под около 300-400% cpu, считай в 4 ядра, при этом были еще «свободные» ядра на 2 x xeon 3xxx 3.0 ghz.

gh0stwizard ★★★★★
()
Ответ на: комментарий от gh0stwizard

Спасибо за подробный ответ! Обнадеживает)
А можешь поделиться стандартными ошибками и граблями на которые новички наступают? Какие-то советы по работе с монгой?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Какие-то советы по работе с монгой?

Выше давали линк по основным граблям. Добавлю следующее, что оптимизацию по шардингу/репликации имеет начинать в двух случаях: 1) ознакомительный 2) когда действительно точно можно сказать, что одна база не выдерживает нагрузку. Шардинг рекомендуется начинать когда объем данных превышает 100Гб, потому что при заливке такого объема монго съест примерно на 30% меньший объем ОЗУ, считай 70Гб. Также следует учитывать факт «покрытия» данных в выборках приложением. Например, база записной книжки контактов в большинстве случаев имеет покрытие в 10% - т.е. из 1000 записей, наиболее часто будешь обращаться к 100 контактам. Что это значит в контексте монго? Это значит, что после рестарта процесса (который при вставке пухнет до максимума) и начала работы с данными, бд закэширует только 10% объем, а не все 1000 записей.

Самый главный совет заключается в проектировании приложения: одна бд это хорошо, а две лучше. Так вот, репликация, шардинг для ненагруженной бд снижают производительность (связано с особенностями синхронизации). Вторым узким местом является отложенное копирование данных на реплики - вся репликация асинхронная. Монго можно настроить на работу как мастер-слейв, так и мастер-мастер. В последнем случае только что записанные данные на одной ноде могут не успеть реплицироваться на другие ноды в момент обращения к ним приложением. Этот ньюанс имеет наиважнейшее, имхо, значение в проектирования успешного приложения с использованием монго.

Покупай книги по монго, смотри лекции и видео от 10gen. В русском переводе есть замечательная книга Кайла Бэнкера «MongoDB в действии» этого года от издательства ДМК. Почему? Потому, что там рассказывается про то, чего не пишут в документации.

Про Perl: если будешь использовать драйвер MongoDB::Async, то в нем не учтен один ньанс: закрытые соединения остаются в режиме TIME_WAIT или CLOSE_WAIT на стороне монго (точно не помню). Чтобы этого избежать нужно добавить три строчки на выставление флага SO_LINGER на сокет на стороне драйвера. Патч могу прислать, если нужно. Зачем это? Затем что, в зависимости от архитектуры приложения, последнее может держать всегда одно соединение или же создавать тысячи быстро закрывающихся. Без патча, можно легко нарушить ulimit и забить пул монго лишними соединениями (по дефолту максимим 20 тыс).

Меньше думай об оптимизациях на первых этапах :) Переделывать схему бд в монго является обычной эволюцией приложения, не стоит думать, что вот сейчас я за день все прочитаю о монго и спроектирую сразу же идеальную схему. Многие ньансы всплывут, я уверен, на этапе первых расширений функционала за счет добавления новых полей в документах. Первой твоей оптимизацией будет уменьшение объема данных в документах: начинается он с выбора ключей меньше 3 букв. Второй оптимизацией является выбор индексов: несмотря на то, что все говорят о размере оных в ОЗУ (а весят они всегда меньше общего объема), тебе придется опираться на основании запросов, которые будет формировать приложение. Иногда лучше делать удобный запрос для приложения, например, по множеству полей (имя, время, иные критерии), чем исходя из мысли, что такой индекс будет много весить и делать выборку по простому индексу, скажем только по времени.

Дальше тебе подскажет опыт, куда двигаться. Если есть вопросы - спрашивай.

gh0stwizard ★★★★★
()
Ответ на: комментарий от gh0stwizard

Спасибо! В общем, буду разбираться, пробовать. Если возникнут вопросы - спрошу.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от gh0stwizard

Возник еще вопрос. Пока не понимаю, как делать update сложных структур. Допустим, есть документ вида:

{"_id": "12345", 
 "field1": {
                 "subfield1": {
                                      "subsubfield1": 1,
                                      "subsubfield2": 2
                                       ...
                                     }
               }

}
Вопрос (Perl):
Как добавлять новые subsubfield'ы, если их нет (!), а если уже есть - обновлять значения?
Надо делать $push?
Как указать, что хочу добавлять именно В field1.subfield1.subsubfield2 ?
В общем, пока что не совсем понимаю, как обращаться к вложенным полям.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Обращения в консоли через точку. Например, mylhash.key1. В перле в зависимости от типа будет ссылка на данные: хэш, массив или строка.

Обновление данных делается двумя способами: update () или save (). Первый способ является менее ресурсоемким, т.к изменяет лишь указанные поля. Второй перезаписывет документ полностью. И также update позволяет изменять пачку документов.

my $doc = $coll-> find_one ({name=>"winner"});
return if ! exists $doc->{_id};
$doc->{age} = 12; #  добавили или изменили поле
$coll-> save ($doc, {safe => 1});


#  тоже самое

 $coll-> update ({name => "winner"}, {'$set' => { age => 12}}, {safe => 1}); # $set это операция в монго

Больше инфы найдешь в доках. С телефона неудобно писать :-)

gh0stwizard ★★★★★
()
Ответ на: комментарий от kovrik

А и главное. $set может принимать и хэши, массивы в виде ссылок в качестве значений. монго неважно есть что то в массиве или хэше или нет, т.е инициализировать структуру не надо. Возможно понадобится делать поиск на наличие пустых данных и проверка ключа на null более удобна чем остальные способы. Просто используй анонимные массивы и хэши как ты привык в перле для выставления или изменения данных: { '$set' => { parent => {subkey => «value»} }}

gh0stwizard ★★★★★
()
Ответ на: комментарий от gh0stwizard

Спасибо за ответ, буду пробовать)

kovrik ★★★★★
() автор топика
27 августа 2013 г.
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.