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

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

9. вы неверно поняли, как раз в примере с блогом, предлагают в роли документов хранить посты вместе с комментариями, а можно и просто посты, а комментарии в отдельной коллекции. Как раз оверхеда минимум и нагрузки на сервер тоже. Отсюда и выходит 10й пункт, если пост мы загружаем целиком, то никаких дополнительных запросов/подзапросов/данных не нужно.
Этот пример с постом и комментариями — самый хреновый пример для embeded записей. Во первых невозможно будет изменить порядок вывода комментариев например изменить сортировку. Во вторых невозможно сделать pagination, ибо делать skip и limit для embeded коллекций нельзя.
В разработке тикет про виртуальные коллекции, так что скоро будет можно.
По мне так концепция хренова -как хранить авторов комментов, например? В смысле не хранить, а как выбрать комменты одного автора?
Чем мне нравится монго — так это тем, что нужно думать, когда проектируешь структуру базы. Перед тем как что то делать, подумайте — а нужно ли мне будет выбирать все комменты одного автора? Нет? Смело делайте их вложенными. Да, но редко (статистика к примеру раз в сутки) — тоже делайте вложенными, map/reduse выберет вам все, что нужно без каких либо проблем. Комменты отдельного автора это самый главный функционал в вашей системе? Делайте для этого отдельные объекты. Вот так вот просто
Лучше хранить в документе с блогом и комментарии к нему.
И отдельно сохранять комментарий автора в другой коллекции.
(это если есть большие нагрузки)
что за бред? в реалиях живущего проекта мы, увы, не можем утверждать, что сегодня комментарии — это всего лишь писульки к посту, а через пол года не будет выборок по по автору.

И такие изменения ТЗ на лету происходят у всех (и это нормально). С логикой принимать решения на старте и тут же всё «бетонировать», проект достаточно быстро зайдёт в тупик.
> Да, но редко (статистика к примеру раз в сутки) — тоже делайте вложенными, map/reduse выберет вам все, что нужно без каких либо проблем.

как это — без проблем? это же full scan. такие запросы получаются очень дорогими.

Ключевой ли это функционал — часто не скажешь. Например, на Хабре это есть. Ключевой ли он тут? Вряд ли. Но если каждый запрос этой страницы будет приводить к полному сканированию всего контента сайта — это однозначно уложит систему.

Я, кстати, не вижу нормального решения для выборки всех комментов автора средствами MongoDB, кроме денормализации — хранения предгенеренной копии всех комментов автора в отдельной коллекции авторов.
{
    title : "My First Post", author: "Jane",
    comments : [
        { by: "Abe", text: "First" },
        { by : "Ada", text : "Good post" }
    ]
}
db.blogposts.find( { "comments.by" : "Ada" } )
Сделать примитивный MapReduce запрос, который на MongoDB будет весьма быстр даже на больших количествах постов-авторов-комментариев.

Тут стоит понимать что при работе NoSQL нужно существенно перестроить мозг с реляционной модели и SQL-like запросов, на слабо-структурную с map-reduce выборками.

Грубо говоря, в отличии от SQL где ты в функионально-декларативном стиле описываешь ЧТО тебе нужно получить, тут ты задаешь правила КАК работать с данными.

Я, извините, добавлю, что не стоит забывать про стоимость ресурсов.

MapReduce распараллелит ваш fullscan, и он (один) будет выполнен сильно быстрее. Но при этом он останется фуллсканом и суммарно нагрузит дисковую подсистему вашего сервиса, как минимум, не меньше, чем раньше.
То есть итоговая нагрузка от миллионов запросов в день (что типично для веб-сайта) в лучшем случае не уменьшится, а скорее всего — несколько возрастет.
Все верно. Но для одного сервера. А делать map-reduce на одной единственной ноде — весьма пошло :-)
Не понимаю, хоть ты тресни, почему для нескольких серверов суммарная нагрузка изменится. Объясните, пожалуйста.
Да, MapReduce позволит вам быстрее получить ответ на один тяжелый запрос. Но если запрос к системе порождает фильтрацию результатов из 1 Тб данных на лету, то основное время тратится на чтение данных с диска. И как ты запрос не параллель, общее количество «диско-секунд» ваша дисковая система потратит одинаковое. Это еще в лучшем случае, без учета оверхеда.
Я имел ввиду если нод много то и дисков много — следовательно суммарный diskIO всей системы выше.
Т.е. тот же террабайт данных и будет фильтроваться, но параллельно (!!!!) на большом количестве нод (какие кроме всего имеют кеш в памяти) и легко масштабируемо.
Иными словами ценой какого-то не очень весомого оверхеда достигается распараленивание того, что в классических моделях хранения данных паралелится из вон рук плохо — дискового IO
1. По запросу вы получаете массив комментов, дальше на клиенте делайте с ним все, что угодно. Не факт что это будет медленне, чем сортировка в том же mysql.
2. Пагинация в комментах выглядит как то бредово.

В общем случае я понимаю ваше негодование, ведь используя монго очень сложно предусмотреть все варианты использования одной и той же бд из за чего при плохом проектировании со временем могут возникнуть проблемы. Но ко всему нужно подходить с умом, тогда никаких проблем у вас не будет.
Икс тысяч комментариев, а потом сортировочку по дате и фильтрацию по автору, и пагинацию, первые 10 комментариев, потом с 11-го по 20й и тд… (с чего вдруг бредово — не ясно, посмотрите на хабр. бредово не давать посмотреть свои комментарии или убрать пагинацию)
db.posts.find({}, {comments:{$slice: [20, 10]}}) // skip 20, limit 10
Спасибо, появилось только в 1.5.1, а я как раз искал это в 1.5.0 версии.
> появилось только
Год уже как прошел.

> Во первых невозможно будет изменить порядок вывода комментариев
Порядок вывода можно легко изменить в приложении, как раз когда нужно будет выводить.
Про порядок, который легко изменить — расскажите, пожалуйста как это сделать, если коментариев хотя бы 100 и нужна pagination? Лично кроме как грузить всю коллекцию, сортировать на сервере, а потом кусок отдавать — по мне это совершенно неприемлимо.
Пост — да, наверное, можно организовать единым хешом со всеми комментами, проблем будет меньше, чем с целым блогом.
Но я говорил про немного другую проблему: появился популярный пост и все бросились его комментировать. В результате и сама структура (пост+комменты) получается большой, и при каждом следующем комменте она перезаписывается или полностью или, в лучшем случае, в среднем наполовину (комменты — дерево, новые не аппендятся в конец).
В результате такие записи становятся узким местом системы, чего в случае РСУБД мы не наблюдаем.
Почему не аппендятся? Добавить новый комент в конец поддокумента — одна команда. Не вижу зачем надо переписывать весь документ (ну или даже половину)
Команда-то одна. Но вот что именно она делает?

{
title: «My First Post», author: «Jane»,
comments: [
{
by: «Abe», text: «First»,
comments: [
{
by: «Abe», text: «First»,
comments: [
{ by: «Abe», text: «First» },
{ by: «Ada», text: «Good post» }
{ by: «Monster», text: «NEW COMMENT» }
]
},
{ by: «Ada», text: «Good post» }
]
},
{ by: «Ada», text: «Good post» }
]
}

Если хранить всю иерархию комментов в одном большом BSON, то добавление NEW COMMENT к этому посту — это вставка подстроки куда-то в середину строки. А для этого надо либо переписать полностью BSON, либо, если движок поддерживает (во что я не верю), переписать только хвост — от NEW COMMENT и далее.
У Вас настолько большая нагрузка, что приходится задумываться о способе сохранения сервером данных?
0_0
Безусловно.

Не заставляйте меня думать, что MongoDB подходит только если вам наплевать на цену любой операции.
Используйте плоскую структуру, если вставка элемента в поддокумент занимает критично много ресурсов
Конечно, Вы правы, именно так. Собственно, вернулись к реляционной модели хранения.
Хранение всего дерева в строке тоже легко реализуется средствами РСУБД — денормализация путем создания BLOB-поля в таблице постов.

Хочу пояснить: посты с деревом комментов — это их собственный пример. Мне показалось, что он несколько надуманный и что документно-ориентированное хранение не дает особых преимуществ в эффективности по сравнению с РСУБД.

А MongoDB, как и другие NoSQL, особенно хорош в случае простой схемы данных (в идеале — одна таблица).
Этот пример вполне себе работает до определенного момента. И обычно критичным становится не ресурсы на вставку, а время преобразования при выводе.

И вообще, этот пример приводится скорее как иллюстрация отличия монги от реляционных СУБД, а не прямой призыв к действию. При проектировании надо головой пользоваться, а не оголтело пользоваться примерами
Я Вам еще один умный вещь скажу, только Вы не обижайтесь: если мне неважна нагрузка, то меня устроит и дефолтная инсталляция MySQL.
NoSQL рассматривают как раз когда нагрузка высока. Точнее, даже очень высока.
Ну если у Вас нагрузка НАСТОЛЬКО высока, что Вы оптимизируете вставку в поддокументы, используйте одноуровневые документы, там накладные расходы меньше
>NoSQL рассматривают как раз когда нагрузка высока.

Не только, иногда scheme free имеет решающее значение. Пускай даже в пару раз медленнее MySQL, но нет необходимости извращаться с пользовательскими атрибутами.
Расскажите, пожалуйста, когда важна schema free? Сходу не могу придумать пример.
Любая система, где пользователь (администратор) может вводить произвольные атрибуты сущностям и/или создавать новые типы сущностей. Можно, конечно, и на РСУБД реализовать, но все реализации что видел производили впечатление монстров — несколько таблиц, трехэтажные JOIN для получения любого объекта и т. п.
Хотя бы интернет магазин с разными типами товаров, когда надо каждому товару разные свойства задавать, а потом еще и сравнивать в каждой категории товар.
Например у палатки надо указать ширину, высоту, длину, водонепроницаемость и т.п.
А у настольной игры: кол-во игроков, время игры, возраст
А у газовой горелки: тип используемого топлива, время работы от одного баллона и т.п.

В mysql действительно громоздко все это реализуется. В документо-ориентированной структуре гораздо проще.
НЛО прилетело и опубликовало эту надпись здесь
Попробуйте, вещь классная. Совсем другая идеология и принципы проектирования баз данных. Вместо MySQL повсеместно :).
Вы оперируете JSON'ом (binary, что, в принципе, одно и тоже для вас как для разработчика), имеете более высокую производительность, поддержку геолокации, шардинга. Платите за это вы невозможностью построения тяжелых запросов, и как следствие, остаточностью в БД.
я бы так не восторгался.

обращение по PRIMARY MySQL в суммарном времени быстрее чем выбор записи из коллекции Монго
о с использованием Хандлерсокетов — вообще космический прирост

Если говорить о шардинге (вертикальном) — то для MySQL есть как минимум три туззы организации вертикального шардинга.

Как таковой Автоинкремент отсутствует, надо либо использовать аналог UDF, либо аналог сиквенсов. Использование _id 12 байтный индикатор не всегда удобно.

В Монго можно использовать аналог Хранимых процедур, работает на ура в родном клиенте, а вот из РНР вызывается это как-то через жопу.

надо просто сперва научиться использовать с умом MySQL, ну а потом уже понять чего в нем не хватает и искать альтернативные решения.
А вот, кстати, не осветили тему в видео: как в MongoDB гарантируется уникальность _id в системах с шардингом?
Генерация ID это работа драйвера. И алгоритм драйвера отвечает за то что бы ID был уникальным.
Спасибо, но не понял, поясните, пожалуйста.

Вот у нас есть три шарда для постов с комментами
1) «А» < Author < «И»
2) «И» < Author < «П»
3) «П» < Author

Добавления ведутся во все три шарда. Новые посты появляются с новыми _id.
Так вот вопрос — кто гарантирует уникальность _id в рамках всей системы?
— Есть единый sequence на все шарды? тогда он — узкое место системы при инсертах.
— Или множество _id разбивается на набор непересекающихся подмножеств? например, деление по модулю числа шардов. но тогда решардинг становится просто адовой процедурой, изменяя чуть ли не все _id в коллекции.
— Или еще как-нибудь?
Хм. Просто, но работает. Спасибо.
В том-то и дело, что не стоит сравнивать шардинг MongoDB и MySQL. Потому что в MongoDB хранятся документы, и выборка идет, соответственно, по документам. Часто можно иметь такой документ для поста, к примеру:

{content: «тестовый пост»,
author_id: bj3123biu12b,
tags: ['тест', 'пост']}

И дальше если вы разбиваете эту коллекцию на две — вы все равно можете спокойно искать как по _id поста, по id автора так и по тегу конкретному, и вставка будет идти лишь в одну из шард., и это будет один запрос и выборка тоже будет лишь в одной из шард.

В случае же MySQL если вы пользуетесь JOIN'ами — так просто табличку не разнести на 2 сервера.

Это я к тому, что эти базы для разных задач и говорить «выучи mysql у него это всё есть», при этом делая вид будто mongodb не нужна — как минимум некорректно.
Я вот после первого знакомства восторгался, а потом понял что лучше иметь две базы, одну со схемой, другую без. Отношения кроме «содержит», «почему-то», слабая сторона NoSQL.

Если есть сущность типа «комменты» как бы содержащаяся внутри сущностей типа «пост», «юзеры», Эдата", то эффективная выборка возможна только по одному типу «родительской» сущности, запланированной заранее. Отношения типа JOIN… ON в общем случае не эффективны, имхо.
>Отношения кроме «содержит», «почему-то», слабая сторона NoSQL.
Эта слабая сторона — в некоторых случаях становится сильной. noSQL надо использовать там, где действительно это надо.

>Я вот после первого знакомства восторгался,
я тоже…

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

хотел использовать во фронтэнде, в качестве информации о товарах, скорость у мускуля оказалась выше. Может что-то в настройках :), может не хватило памяти…
документация для изучения — достаточно приличная
Я сам его пока не ел :)

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

Можно жить и без JOIN. Но грустно:
select id from Companies where domain like '%.ru';
select name from Staff where company_id in (большооой массив интов);

Это не критично, но неудобно в больших базах: запросы в мегабайт размером долго передаются, долго парсятся, делают логи плохо читаемыми.

Главный плюс, который я увидел в MongoDB — п. 18, т.е. выход за пределы in-memory DB. Это воодушевляет.
Индексы mongodb действительно выгружает в память, мало того, норовит часто используемые данные выгружать в память тоже. И в нем нету т.н. опции «memory limit». Из-за этого лучше сразу монго ставить на отдельный сервер.

Я как раз с этим недавно столкнулся, когда размер БД превысил 1 гигабайт, у меня начали свопиться веб-сервер и часть кучи монго, из-за чего сильно упал перфоманс. Знаю, что у foursquare были подобные проблемы. У них монго висел на сервере с 64гб оперативки, и они как-то пропустили момент, когда размер БД превысил обьем оперативки, из-за чего уходили в даун.
Можно попробовать ограничить память и процессорное время с помощью OpenVZ в рамках одного сервера.
Вот это беда, конечно. Держать все базы в RAM не очень хочется — дорого.

Надеюсь, что им удастся в будущем реализовать эффективное кеширование и страниц данных, и страниц индексов.
Мне нравится то, что в монге можно и нужно делать простенькие денормализированные кэши. Типа «количество комментариев к сообщению». И апдейты таких штук будут атомарные.

Вот только Django-проект средней величины перевести с мускуля на монгу сложновато. Да и биндинги к сожалению не официальные.
По поводу пункта 10 — насколько я помню, операция JOIN в реляционных базах данных неэффективна в случае, когда мы имеем дело с данными, которые разбросаны по кластеру серверов, да и в рамках одного сервера очень дорогая. Мне кажется, что решение предлагаемое MongoDb вполне адекватно — мы можем удобно и быстро работать с документами, в которых уже лежат все нужные нам данные, и которые находятся на разных шардах.
А как вы будете работать, если понадобится выбрать не все комменты определенного поста, а все комменты определенного автора? Денормализация с дублированием данных до какого-то уровня или проход по всем шардам в поисках автора?
Суть MapReduce.
В рамках одного сервера она практически бесплатная при наличии индексов.

В случае с MongoDB придется либо (в хорошем случае) хранить подчиненные записи в этом же хеше, либо (как в примере со всеми комментами данного автора) делать full index scan (при наличии индекса по author_id, иначе — full table scan).
Предположение MongoDB такое: запросов второго рода у вас почти никогда не возникнет. Мой опыт работы подсказывает, что возникает, да еще как — функционал сервисов часто совсем не так прост, как хотелось бы программисту.

Но в своей нише (простая схема данных, простые запросы, очень выссокая нагрузка) MongoDB смотрится вполне достойно.
Мы используем MongoDB в продакшене. Выбрали из-за исключительной способности к горизонтальному маштабированию.

Я собрал ReplicaSet из трех серверов и теперь спокойно переживаю выход из строя/перезагрузку одного из.

Слабое место — отсутствие транзакций.
У вас много отношений «многие-ко-многим»?
Не могу припомнить хотя бы одно.
А проблему с недоступностью монги во время выбора нового мастера как решили?
Мое дело было создать ReplicaSet. Дальше все работает автоматически. Периода недоступности во время смены мастера мы не замечали. Нагрузка однако не большая. Около одного запроса в секунду.
а платформа какая? и драйвер?
CentOS + MongoEngine
Ясно, спс
спасибо за информацию, очень полезно
Очень напрягает, что нету атомарных операций вставок нескольких объектов в коллекцию, приходится мириться с возможной неконсистентностью в mongodb при одновременной вставке нескольких зависимых объектов.
Как я думаю, неконсистентность может возникнуть лишь в случае, когда сервер упадет во время вставки. Таки вообще не проблема. Держите репликасеты и обратывайте ошибки вставки (пытайтесь по новой).
Не факт, вставка одной записи может пройти, а второй нет. Вроде бы нет механизма гарантирующего вставку двух записей. Или есть?
Ждать ответа базы после вставки каждого документа? (параметр safe в операции insert)
Целостности не гарантирует. Может упасть БД после вставки первого документа. Может само приложение. В общем первый документ операции пройдёт успешно, второй — нет. Но первый без второго нарушит целостность.
В таких условиях какая либо другая БД может гарантировать целостность?
Транзакции как раз и гарантируют такую целостность
А вообще да, я тут подумал, что это будет слишком сложно как-то. А что за надобность держать два разных документа, а не один?
Это в реляционке без этого не обойтись, а у нас всё же rich documents :-)
Денормализация например.
Ну тут надо было на этапе дизайна думать :-)
На некоторых задачах думай — не думай, но или делать денормализацию, или половина запросов будут читать всю «таблицу».
Видимо, поэтому и рекоммендуют хранить более крупные документы, чтобы все данные хранились в одном месте, и не приходилось обновлять более одного за раз для поддержания целостности.
Наверняка не всегда применимо, конечно.
11. репликационный лог — создается отдельная таблица на мастере, в которую записываются все действия пока не закончится указанный размер лога (по умолчанию 11 Гб). Тоесть для небольших баз данных вы можете полностью востановить данные даже если мастер и слейв сломаются. Но вообще это сделано для более быстрой синхронизации в случае временного дауна слейва.
12. Довольно интересная фича кстати. (replica sets называется), но использовать при конфигурации один мастер + 1 слейв по личному опыту не рекомендую. больше проблем чем профита
Объем оплога по умолчанию — 5 процентов ёмкости вашего НЖМД.
а в чем проблемы со схемой мастер+слейв?
при использовании replica sets мастер выбирается посредством голосования. Тобишь если у вас всего 2 процесса, каждый голосует за себя, ничего не получается. Для этого нужен 3 процесс «арбитер». обычно его запускают на одном сервере с предпологаемым слейвом, но если этот сервер падает, то падают сразу 2 процесса, и, если, по каким либо причинам предпологаемый мастер был слейвом, то вся система падает. И ничего с этим сделать не получится)
Арбитр рекомендуют ставить не параллельно со слейвом, а на сервере с приложением.

И в драйвере есть возможность указать несколько серверов, с которыми последовательно будет пытаться соединиться приложение.

У нас схема с арбитром вполне работает, но я поэкспериментирую ещё с 3-full-node схемой
Очень настораживают следующие пункты:
Мастер-репликация и 10-20с на выбор нового мастера. Если «старый» мастер из за перегиба провода будет доступен периодически, целостность данных по кластеру будет пропадать на 10-20с в каждое его пропадание из сети. Надеюсь, у «предыдущего» мастера при выборах нового нет преимуществ.

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

Отсутствие полнотекстового поиска несколько настораживает, и намекает, что ранжированием и алгоритмам соответствий и нормализации надо будет место в коде приложения.

Остальное, типа как неофициальные клиенты для разных языков и отсутствие транзакций — не так страшно.
До сих пор выбираю между Riak и Mongo.
У обоих есть достоинства и недостатки.
Хороший вопрос про «мигающий» мастер, но, как мне кажется, идеального решения здесь вообще нет.
Предположим, кластер развалился надвое и между половинами потерялась связность. Каждая половина уверена, что она консистентна и живет со своим мастером. И обе они могут изменять свои данные. Если после этого связность восстановится, то что ни сделай — будет плохо, кластер не восстановить.

Нормализацию можно делать, указывая в полях _id из другой коллекции.
Но тогда придется передавать списки id через приложение, как я описал здесь, что весьма коряво.

Про полнотекстовый поиск (Sphinx) рассказывал Аксенов на той же конференции. Можете посмотреть видео, я пока не стал — пишут, что очень скучно.
Ну, идеальное решение — репликация без мастера (Riak).
А целостностью жертвуют в пользу A и P.

Нормализация по id да, но тогда либо будет довольно монструозный MapReduce запрос, либо придётся делать второй запрос на получение связанных данных. С Riak'овскими link'ами получается попроще.

Да, Sphinx-то можно к чему угодно привязать в принципе.
Когда старый мастер проявляется от становится slave.
Не знаю у кого там 10-20, у меня секунда за три выбирается максимум.
>> Это имхо хреновая идея — нагрузка на один элемент может стать критически большой.
Возможно внутри документ разбивается на отдельные сущности.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории