Pull to refresh

Comments 211

У меня только один вопрос, по мнению автора, реально ли всё таки подобный сервис реализовать на Монго (и как тогда сохранять данные) или 16МБ это приговор для подобного применения БД и все танцы вокруг этого лимита это плохо и так делать не надо?
Конечно не приговор, если правильно моделировать данные. На вскидку, я бы под хранение сериала завёл такие коллекции:
  • serials — для хранения данных непосредственно о сериале (название, описание, etc.)
  • episodes — для хранения данных о каждом эпизоде
  • series — под отдельные серии
  • actors — для актёров
  • reviews — здесь хранить обзоры

То есть сделать вполне себе реляционную схему данных.
Есть подозрение, что будет тормозить, особенно когда надо показать все эпизоды сериала.
Предложенная мной схема совершенно не противоречит тем рекомендациям, которые дают разработчики MongoDB. Стас, прочтите пожалуйста те материалы, на которые я расшарил ссылки в моей статье. Иначе у нас не выйдет конструктивного диалога.
Никто не спорит, что в монге можно сделать реляционность. Вопрос в том, что для чего-нибудь вроде классической страницы сериала на IMDB у нас уже не получится одним запросом в монгу взять документ «сериал» и из него все нужные данные, а придётся городить серию SQL-подобных запросов в духе «а дай ка мне, монга, из коллекции актёров тех, кто снимался в вот этом сериале, ок, а теперь из коллекции эпизодов те, которые из этого сериала». Т.е. мы уходим от идеи документа, описывающего всю сущность к тем же реляционным связям между таблицами и тут возникает вопрос нафига нам монга.
Вы путаете возможность хранить вложенные документы и обязательность хранения всех связанных данных в виде вложенных документов. Так вот, MongoDB даёт такую возможность и не в коем случае не требует хранить все данные в виде вложенных документов. Вложенным документов весьма удобно хранить какие-то вспомогательные, информационные данные. Ещё удобно выделять во вложенный документ группу полей, логически связанных между собой. Как пример, можно привести блок Technical Specs, с той же IMDB.

И повторю в который раз, что идея документа, описывающего всю сущность — это очень плохая идея. Вы читали рекомендации, на которые я ссылался в своей статье?
Чем же тогда монга лучше реляционных баз?
В контексте данного треда тем, что реляционные базы такой возможности не дают.
Почему не дают? Сериализуй в json\xml любые данные и храни в базе. Схемы то нет, то есть базе по большому счету пофиг что ты в нее положил. А в PostgreSQL есть возможность работы с JSON, по скорости превосходящая монгу в некоторых сценариях. Так что при подобном рассуждении реальная полезность монги уменьшается почти до нуля.
Распространенный аргумент. Отличие в том, что монга понимает внутреннюю структуру такого json'a, а обычная реляционная база — нет. Нужно изменить схему, нужно по данным сделать какой-нибудь произвольный отчет, нужно проапдейтить в этом документе десяток полей не гоняя его полностью по сети — монга с этими кейсами справляется (для того и создавалась), реляционная база с кем-то где-то сериализованным json-ом в строковой колонке никак не поможет, только загружать все в приложение и там каждый раз разбирать.

Postgress может работать с внутренней структурой документа, да. Только монго для этого создавалась, а для постгреса это еще одна фича.
Ага, еще одна фича, которая работает лучше монги ;) В MS SQL тоже самое можно делать с XML, а в оракле и c JSON.
Так что нет необходимости каждый раз загружать весь документ.
Формально в постгресе практически все есть фича. JSON-фича, xml-фича, hstore — фича. Даже родной pl/sql — это такая же фича. Так может хорошо, когда архитектура система позволяет разрабатывать для нее множество фич и подбирать ту, что наиболее эффективно решает стоящие задачи? Хочешь — работаешь с JSON и хранимками на plv8js, хочешь — с реляционными данными. Когда нужен эффективный ассоциативный массив — есть hstore,
UFO just landed and posted this here
1) Совету почитать про механизм хранения длинных строк в базе. По секрету: размер записи в базе может быт не фиксирован. А еще есть sparse tables.

2) Любой поиск в монге не по ключу — гарантированный обход всех документов, очень небыстрый. Можно создать индекс, как в РСУБД. Но монга для эффективной работы должна данные и индексы держать в памяти, то есть большое количество индексов увеличивает требования к железу.

3) Встроенный map reduce сильно недотягивает до материализованных представлений в СУБД.

4) Да, монга уже сожрала много реусурсов (все держит в памяти), поэтому допрасходов на агрегатные запросы не должно быть. А РСУБД стараются не пожирать ресусры без необходимости и умудрются обрабатывать базы, в тысячи раз превосходящие объемы памяти сервера.

5) Про курсоры смешно. Пока вы держите курсор — вы держите лок на всей коллекции документов и все операции записи курят. РСУБД как раз стараются как можно быстрее выполнить свою часть работы, чтобы можно было обработать следующий запрос.
UFO just landed and posted this here
Выкиньте MySQL, в нем по факту нет оптимизатора запросов. Вы как написали джоин, так он тупо его и выполнит, может даже индексы не задействовать. Переупорядочивать джоины и использовать статистику не умеет.

Возьмите хотя бы PostgreSQL или взрослые СУБД типа Oracle (на *nix) или MS SQL на Windows. Если же у вас данные гарантированно влезают в ОП (что наиболее вероятно, если вы используете Mongo), то вообще берите SQLite.

И не ведитесь на булшит про состоявшийся инстрмент. 90% использований монги — кэш, то есть не основное хранилище данных. В сценариях кэша как раз можно забить на консистентность и на проектирование, потому что в кеше хранят данные, не требующие дополнительной обработки и надежности хранения.
Но я бы в качестве кэша рекомендовал Redis. У него меньше хрени, типа локов на коллекции. Но многим направится возможность писать запросы с предикатами, можно не париться с ключами.

Очень мало кто использует монгу как основное хранилище, особенно социальные сети. Вам еще не приходила в голову идея, то вы назовете 5-10 соцсетей и там не будет монги как основного хранилища 100%, а в половине монги не будет вообще.
Ну зачем так сразу сливать? Ваша схема, вам и доказывать что она эффективна.
Реляционную в разрезе таблиц — почему бы и нет? Зато можно в коллекции будет класть не линейные структуры, если понадобится.
По отображению всех эпизодов сериала, я так полагаю, вас смущает, что для каждой серии нужно будет запрашивать актеров и обзоры? Но ведь фактически это два запроса: запросить всех актеров where эпизод in (…), запросить все ревью where эпизод in (…), а раскидывать связи с конкретными эпизодами и т.п. на среднем слое.
Предлагаю сделать тестовый пример и сравнить.
Не возражаю, только потом выложите результаты.
Я сделаю на MS SQL, а с вас тест для монги.
Show. Это называется TV Show. Serials — это что-то про последовательный порт.
Спасибо, за столь ценную и релевантную информацию! Но вдруг что, Wikipedia.
В статье написано:
Serials contrast with episodic television, with plots relying on a more independent stand-alone format. Procedural drama television programs are commonly episodic

И еще:
Though some American television shows have introduced serial elements into their narratives, episodically numbered serials are rare in modern US television. They are generally used within episodic series to generate ratings spikes, and are usually limited to two parts.

Т.е. «serial» это нечто весьма узкое (многосерийный телефильм?), по сравнению с тем, что обычно понимается под русским понятием «телесериала». Т.е., например, «Семнадцать мгновений весны» — это serial, а «House» — нет. Хотя по-русски оба — телесериалы.
A television series that is intended to comprise a limited number of episodes may be called a miniseries or serial. Series without a fixed length are usually divided into seasons or series, yearly or semiannual sets of new episodes.

->TV show
На самом деле в MongoDB учебных материалах показывали, как круто, когда вместо кучи связаны таблиц — одна коллекция документов.

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

Также, возможно, все обзоры можно тоже вставить в описание сериала, если анализ покажет, что не бывает 18 тысяч обзоров на сериал в целом. Обзоры и комментарии к каждой серии можно хранить в серии же (опять же, если анализ позволит).

Это с точки зрения хранения данных. А с точки зрения удобства анализа надо смотреть дополнительно.
Теперь логично было бы увидеть статью, в которой описано как сделать правильно. На том же примере с соцсетью или сериалами.
ок, я напишу перевод на английский и будем ждать статьи от Сары.
Вы предлагаете автору обратить внимание на материалы, которые появились позже написания статьи. Появление многих из них и было вызвано статьей, как мне кажется :)
Сомневаюсь, что Сара прочитаю статью на Хабре. В первую очередь, статья написана для тех, кто прочитал ваш перевод. MongoDB развивается очень динамично. И главное — это показать неактуальность статьи Сары. А то, когда она стала неактуальной — это уже второстепенный вопрос.
Кстати, хочу вас попросить разместить ссылку на мою статью в вашем переводе. Думаю это будет очень полезно для читателя.
Сорри, но полезности не вижу. Вижу кучу ничем не подкрепленных утверждений (ни примерами кода, ни опытом реальных проектов) и обвинения автора в некомпетентности, в комментах к переводу много таких.

Сделаете пример — с радостью сделаю апдейт к переводу.
Попробую вбросить.
Если опустить проблемы масштабирования реляционных баз данных и взять, например, постгрес, зачем мне монга, когда я могу написать что-то вроде
create table foo (id serial primary key, data jsonb);

И получить ту же монгу? Помимо прочего я могу не динамические поля вынести из data и использовать мощный sql для работы с ними (а могу и не выносить, но тогда не будет строгости)?
Postgres действительно очень крут. Но про масштабирование вы и сами написали ;)
А вы сможете по какому-нибудь подполю этого поля data делать поиск?
Ну, какие-то индексы в последнем постгресе появились, конечно: www.postgresql.org/docs/9.4/static/datatype-json.html # 8.14.4
Так что, я думаю, да, для части задач postgres можно рассматривать теперь как альтернативу mongodb.
Да, я говорю о 9.4, там есть индексы специально под jsonb(по бенчмаркам автора они работают лучше монговских, потому что в монге взяли btree и никак не меняли), есть специальный синтаксис для выборки по jsonb/json, также есть hstore(хеш строка-строка) и array любой вложенности, также с возможностью добавлять индексы.
И получить ту же монгу?
Это не так удобно как монга, + тут есть сложности обновления данных, например инкремент json поля или операция find&modify, upsert. Это так же отрицательно сказывается на скорость.
А зачем это здесь? Я разве написал статью о том, что MongoDB лучше Postgres?

Также непонятно, почему сравнивается самая свежая бета-версия Postgres, а MongoDB взята стабильной версии 2.6. Почему не 2.7, в которой очень много улучшений, в том числе document level lock?
Через полгода будет уже не бета, в разработке можно было даже Alpha3 использовать спокойно.
Потому что в новых проектах не вижу смысла в монге даже как в промежуточном хранилище. Есть Sphinx, Lucene/Solr/Elastic, которые ко всем прочим плюсам монги еще и поисковые машины.
Не в 2.7, а в 2.8
И это новый тип хранилища, старое просто не переведете.
2.7 — это девелоперская версия 2.8. И это не новый тип хранилища, а возможность подключать другие типы хранилищ. Простите, но вы демонстрируете совершенное незнание MongoDB.
Вы это читали?
MongoDB 2.8 includes support for two storage engines: MMAPv1, the storage engine available in previous versions of MongoDB, and WiredTiger. By default, in 2.8 MongoDB uses the MMAPv1 engine.

А про совместимость обоих движков?
WiredTiger supports all MongoDB features and can interoperate with MMAPv1 in the same replica set or sharded cluster, but requires a change to the on-disk storage format.
Вы это читали?

Я имел в виду, что document-level locking поддерживает только новый тип хранилища, я нигде не писал, что старый уберут.

А про совместимость обоих движков?

Да, нужно взять все данные и сконвертировать / перезалить в хранилище WiredTiger.

Что из этого следует-то?
А как можно было догадаться, что вы имели в виду document-level locking? И из этого абсолютно не следует, что:
старое просто не переведете
В том же абзаце описано два очень простых способа сделать это.

Хейтерство вредит вашему здоровью ;-)
Ok, про сложность конвертации был не прав.

Итого — добавили «мегафичу» row-level lock, которая является обязательной для любой уважающей себя RDMS.
В следующем релизе добавят транзакции, как я понимаю. Так и до постгреса дойдут со временем.
— Как считал NoSQL промежуточным кешем, так и продолжил, извините, не переубедили.
Судя по дате статья достаточно свежая, а график выхода версий монги сильно отличается от постгреса. Монго 2.6 — 7 апреля 2014, постгрес — 9 сентября 2013.
Асемблер быстрее Си
Си быстрее java
Java быстрее ruby, python, php
А можно пример сервиса (модели данных/сущностей), для которого монго подходит лучше реляционных баз? Потому что логика защитников строится обычно как-то из разряда «хотите реляционность и транзакции — используйте инструменты, подходящие для этого». С утверждением-то самим по себе спорить трудно, но в чем преимущества монги и в каких юзкейсах отсутствие схемы дает больше плюшек, чем те самые транзакции и cascade?

Советы в духе «не можете удалить сериал — не удаляйте» тоже, конечно, радуют. Реальный кейс возможности удалить данные — это любая админка же.
Я бы не советовал использовать MongoDB только в случае, если в вашем проекте будут использоваться транзакции (например, какая-нибудь банковская система). В остальных случаях я не вижу проблем с использованием MongoDB. С той же каскадностью там проблем абсолютно нет. Даже наоборот: дополнительно к ссылкам у вас появляются вложенные документы, что позволяет делает схему более оптимальной.

По поводу плюсов. Не знаю почему все так цепляются за отсутствие схемы. Для эта фича стоит одной из последних. Я бы выделил шардинг и репликацию из коробки. Они действительно очень просто настраиваются и очень хорошо работают. Также мне очень нравится то, что данные хранятся и запрашиваются в JSON формате. Полный список фичей можно почитать здесь, не вижу смысла его перепечатывать.
Гипотетическая ситуация: есть телешоу

{"name": "The Newsroom", "_id": 1}


и актер

{"name": "Jeff Daniels", "_id": 1, "shows": [1, 2, 3]}


Роскомнадзор FCC запретила у нас в стране последнее шоу Аарона Соркина как экстремистское и потребовало удалить его с сайта. Как убрать его из атрибута «shows» актера Джеффа Дэниелса и еще десятка людей из каста?
Спасибо за отличный вопрос, очень ждал его. Но как раз эта ситуация весьма наглядно показывает, что удалять-то и не надо. Потому что шоу будет недоступно всего для одной страны, а все остальные пользователи могут и дальше его смотреть.
Я, конечно, сам виноват, что так оформил пример, но хотелось бы получить какой-то общий ответ для тех случаев, когда удалять все-таки надо и для всех. Так все-таки «с каскадностью проблем абсолютно нет», или «вам придется всегда ставить при удалении атрибут deleted: true, делать проверки для каждого результата find и купить бесконечную СХД»?
… где вы говорите, что:

В общем основная идея в том, что в MongoDB ответственность за удаление связанных данных действительно ложится на приложение


То есть (если забыть о кейсе с цензурой) в общем случае у нас два выхода:
— делать пометку deleted. Минусы уже перечислены и вами, и мной: дисковое пространство, ненужная дупликация мусорных данных (в тех случаях, когда их никто не хочет восстанавливать), необходимость постоянно писать что-то типа db.tvshows.find({_id:1, $not: {deleted: true}}) для каждого запроса.
— удалять вручную на уровне приложения. Для определенного уровня связности это работает, но чем больше отношений, тем больше геморроя — шоу придется удалить не только у актеров (которых много), но и у режиссера, у связанных с ним мероприятий, удалить связанные обзоры, related_news, эпизоды и т.д.

И это, по-моему, основной вывод той самой статьи, которую вы разбираете, который там достаточно четко описан — как только у вас в монге возникают ссылки и отношения, то возможно, наступает момент задуматься, что вы используете неправильный инструмент. Потому что если ценность изкоробочного масштабирования для вас не превышает трудность прочтения мануала шардинга для постгреса, а любовь к JSON не застилает глаза вышеперечисленным трудностям, я не вижу больше других причин использования NoSQL там, где SQL отлично и незаметно для приложения справляется со ссылками.
В РСУБД даже при наличии такой возможности ничего не удаляется в нагруженных проектах, ибо удаление стоит дороже, чем дисковое пространство.
А можно, там, пруфы какие-нибудь? Я в хайлоаде не очень и любопытно, конечно.
В статье я привёл пару ссылку на баги, где MySQL просто крашится при подобном сценарии.
На один баг. Первая ссылка — баг MySQL Workbench.
Я как раз сейчас в проекте, где есть Enterprise база в несколько терабайт, где в среднем происходит около четырех логинов в секунду (несколько web серверов) и т.д. Так вот, в этом случае данные все-таки удаляются в базе, так как:
1. Это соглашение с клиентом;
2. В процессе delete, база MSSQL физически не перемещает все-все-все данные, а оставляет пробелы на некоторое время, они могут быть удалены потом. А отсюда ручное проставление флага is_deleted не имеет смысла.
У нас не хайлоад в привычном понимании, а база данных по файловой системе (Oracle/MSSQL), и такой подход имеет место. Там ведь не просто запись в таблице, но и связанные таблицы, триггеры, индексы, создание роллбэк логов и тп вещи. Одномоментное удаление какого-нибудь каталога на десятки тысяч файлов не лучший вариант, учитывая, что надо еще и другие операции делать.
в «нагруженных проектах» обычно ничего не удаляется, потому что либо а) информация может потребоваться как архивная (см. финансовые приложения), либо б) Большой Брат (см. социальные сети).

аргументы про дорогое удаление, перестройку индексов и т.п. легко разбиваются пониманием того, что на перестройку индексов при добавлении записи никто почему-то не скупится.
Тоже хотелось попробовать MongoDB, но не мог найти для себя usercase, где бы можно его было вставить лучше, чем РСУБД.
Почитал эту ветку камментов и понял одно, лучше применение — это логи ) В том или ином виде. Записи произведённых сделок, произведённых действий… Где «что написано пером...».
collection level lock + отсутствие гарантий при записи делает монгу плохим кандидатом для такой задачи.
Стабильный релиз с этой фичей еще не вышел.
Для подобных случаев мы в своём фреймворке, использующим базы MongoDB как основную, все эти заботы переложили на движок, просто при описании моделей нужно указать правильно все связи. Далее при удалении зависимые данные автоматом удалятся тоже, если это разрешено флагом. Этот подход, кстати, со временем приводит к тому, что ошибаться программист становится все реже и реже и финальные тесты порой проходят как по маслу.
Добавить атрибут-массив «страны, в которых запрещено» в коллекцию телешоу.
а если этот актёр участвовал в других телешоу, которые в этих странах не запрещены?
Просили запретить по актеру, а не по телешоу. Если по телешоу — то аналогично в телешоу.
Я сам сторонник РСУБД, но это явно не тот пример.
Советы в духе «не можете удалить сериал — не удаляйте» тоже, конечно, радуют. Реальный кейс возможности удалить данные — это любая админка же.
Вы изменил форму и суть моего посыла. Суть была в том, что на больших проектах даже в реляционных базах стараются избежать удаления сильно связанных данных. Почему это так, я описал в статье.

И про админку вы не так поняли. Мой вопрос был не в том, куда всунуть кнопку удаления, а что может заставить админа на неё нажать?
Я вас вполне правильно понял: ваши кейсы избегания удаления данных все относятся к пользовательскому контенту и делаются исходя из ценности данных. С этим все отлично, но есть ведь еще и исключительно сервисный контент, размещаемый со стороны админов и обслуживающих людей сервиса — новости, события, те же статьи про фильмы и сериалы на IMDB. Их совершенно незачем «помечать как удаленные», потому что это только зря потребляет небесконечное дисковое пространство — особенно когда данные большие. Из живых примеров — в нашем проекте, например, админы занимаются загрузкой на сайт архивов с GPS-данными, которые потом после парсинга могут занимать порядка ~100000 (достаточно связных) строк в базе. Часто возникают ошибки — из-за невнимательности и т.д. — когда приходится все удалить и залить заново. Оставлять данные в базе как «помеченные» как минимум нецелесообразно, потому что зачем?
Совершенно согласен, что отмечать «удалёнными» всё подряд — это неправильно. Но и принебрегать этим подходом также не стоит. Сказать что-то осознанное про ваш пример с GPS мне сложно, так как для этого надо видеть схему ваших данных. В общем основная идея в том, что в MongoDB ответственность за удаление связанных данных действительно ложится на приложение. Но не факт, что тот же MySQL не завалится при каскадном удалении большого количества связанных данных. Как-то так.
А можно пример сервиса
Почти любые веб сайты удобно делать с монгой.
в каких юзкейсах отсутствие схемы дает больше плюшек
Нет проблем миграции данных, более гибкая структура данных дает разные преимущества, за счет разных факторов скорость разработки вырастает.
Вместо транзакций есть 2-х фазные коммиты, которые имею и плюсы и минусы перед транзакциями, они более выгодны для больших данных.

Реальный кейс возможности удалить данные — это любая админка же.
Проблемы удаления нет, целостность поддерживается с помощью 2-х фазных коммитов.
Нет проблем миграции данных

Можно уточнить, почему пропадает проблема миграции данных?
Приведу пример: заказали добавить логики в проект, при этом необходимо добавить полей («колонок») в коллекции, в итоге я пишу код как будто эти поля уже есть, потом код деплоится без «реструктуризации» БД, без остановки проекта — задача решена.
В работе эти поля могут заполнится например пользователями и новый код будет работать с этими полями.

Так же если нужно, можно обновить коллекции (изменить структуру) без остановки приложения.
Если что-то пошло не так — просто откатываем код на предыдущий релиз, без откатывания структуры БД.
С *sql базами это делается сложнее, особенно когда в таблицах лежат миллиарды записей.
А какая проблема добавлять в SQL необязательные колонки? Точно так же без остановки. Точно так же откатывается код назад, если что-то пошло не так. Необязательные колонки не мешают чтению и записи.
Возможно, я неправильно сформулировал вопрос.
Правильно ли я понял, что код приложения вынужден учитывать все возможные «схемы» документа? Если это так, то вместо одноразовых миграций мы получаем переусложнение основного кода приложения.
Нет, у меня чаще это похоже на утиную типизацию, поле есть — значит обрабатываем, даже если на входе будет другой тип (либо ограниченный список типов), например вместо типа «пользователь» будет «менеджер» (который является подтипом первого).

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

  • Легко расширять модель платежа.
  • Легко шардируется.

Давайте теперь по порядку.

1) Банк достаточно крупный, поэтому он имеет огромное количество каналов через которые пользователь может совершать платежи. Все каналы разрабатываются разными командами, иногда даже на разных языках. У всех каналов формат платежей разный. Формат может часто меняться. Поэтому нам было важно иметь гибкую модель данных.

2) Система построена на анализе истории платежей. Shard key был выбран таким образом, что все расчеты по конкретному платежу происходили в пределах одного шарда, что значительно снизило время на обработку одного платежа. Время обработки одного платежа было критичным, так как система должна была работать в real-time режиме и бизнес выделял не более 1 сек. на анализ.

Если будет интересно, могу написать более подробную статью о том, как мы использовали MongoDB. Сейчас этот комментарий выглядит очень сумбурно )
А вам не запрещено озвучить хотя бы порядок кол-ва документов / размер БД и порядок стоимости оборудования под шарды? Даже хотя бы просто конфигурации и кол-во серверов, ценник будет понятен и так.
Просто «легко шардируется» это когда бюджет резиновый. =) Во всяком случае у меня пока такие ощущения.

P.S. В продакшене использую и монгу и постгрес и марию и редис, базы — десятки-сотни гигабайт.
Если честно, то я помню только примерные цифры.

Год назад была вот такая ситуация:

  • Кол-во документов — 60 млн.
  • Размер БД — 200 гигабайт.
  • Индексы занимали порядка 70-100 гигабайт.
  • У нас было 4 шарда, каждый шард состоял из 3 нод (1 мастер, 2 реплики). Все ноды были одинаковые — 4 CPU, 12 GB RAM, 100 GB HDD.


Да, что касается стоимости, MongoDB не дешевое удовольствие. Хотя это наверное все относительно, так как есть и более дорогие решения, например, Amazon DynamoDB. Ну и для банка это были не большие деньги, тем более, система себя очень хорошо показала.
М, ну да, конфигурация вполне скромная, даже на 12 серверов сумма будет адекватная. А из какого расчета брали две реплики под каждый шард? Из задачи вроде получается, что данные восстановимые, а главное скорость обработки?
Для нас были важны две вещи. Во-певрых это скорость обработки, а во-вторых, что бы каждый шард был всегда доступен. Самая минимальная конфигурация репликасета как раз и состоит из 1 мастера и 2 слейвов.
Понятно. Просто если не рассматривать возможность ситуации, когда одновременно выходят из строя два сервера в репликасете, то минимальная конфигурация это 2 настоящих сервера + 1 арбитр, которого можно разместить на любом другом сервере. А так, если нет особой параноидальности, то 8 серверов работали бы также (по скорости), как и 12. Если уж брать 12 машин, то можно было бы сделать 5 шард, в каждой по 2 сервера + 2 на арбитры и mongos. imho.
Да, насчет арбитра согласен.

А как насчет выполнения compact или обновления версии mongodb? В нашей конфигурации, мы просто выводили из под нагрузки одну реплику, на ней все делали и снова пускали ее в бой. Затем так же самое на других нодах. При конфигурации мастер-слейв-арбитр так получиться сделать?
Сразу скажу, что шардинг смотрел только на стенде и обновлять версию не пробовал.
Но теоретически. У вас в каждой шарде было, фактически по 3 сервера. Чтобы обновить монгу вам в любом случае надо это сделать на всех, т.е изначальный master тоже нужно было обновлять — с периодом недоступности. А тогда разницы не вижу. Выводите мастера в offline, мастером становится вторая реплика. В случае схемы из двух серверов + арбитр это даже проще — обновлять нужно 2 сервера, а не 3, т.к арбитр, как вы знаете, не содержит никаких данных.
Так что на мой взгляд проблем быть не должно, а было бы даже проще. Т.е вашу схему можно было безболезнно ускорить, создав 5-6 шард, вместо 4.
Как же всё проще при master-master репликации :-)
А у кого из коробки есть беспроблемный master-master?
Lotus/Domino я знаю, кто еще? =)
Врать не буду — не пробовал. Но выглядит сыровато. И да заминусуют меня… но субд на JAVA? Я лично — не готов. =) Тогда уж CouchDB, и постарше и всё-ж таки erlang. Правда, couch я сам вживую не щупал. Но отзывы слышал хоть какие-то.
С одной стороны да, GC в любой момент может остановить базу «на переучёт». С другой — она может прозрачно интегрироваться в java-приложение, что позволяет реализовывать любую логику обхода графа, которая может потребоваться, с нулевыми накладными расходами на общение с базой.
Возможно вы правы. На тот момент это был мой первый опыт работы с MongoDB и скорей всего, в некоторых местах, я сделал не оптимально. Но, в целом, я остался доволен.
А почему не стали делать на РСУБД? Постгрес с тем же jsonb.
РСУБД мы отбросили по двум причинам:

  • Нам нужна была гибкая структура данных. Мы меняли модель одного документа примерно 1 — 2 раза в неделю.
  • Только искусственный шардинг, иначе говоря, партиционирование.


PostgreSQL рассматривали. Но на тот момент когда мы выбирали БД, у них не было поддержки запросов, с условиями по json. Нельзя было делать индексы по конкретным ключам json.
Конечно же реквестирую статью! :)
Использовал RDF-базы данных (sezam и jena), которые можно считать графовыми, как основное хранилище. Правда объемы данных и нагрузка были не слишком большими.
Выбор был сделан из-за удобного языка запросов.
А можете что-то сказать про Neo4j или OrientDB?
По описанию Neo4j понравилась, но в деле не пробовал. Про OrientDB только слышал.
Neo4j ужасно тормозная, даже на задачах с графами, просто узнать что таблица пуста 300мс, сложно работать с индексами и ещё куча косяков. Применима, наверно, только в очень специальных случаях.

Да идея графов замечательная, но пока нет нормальной реализации.

OrientDB тоже смотрел, но уже даже и не помню чем все закончилось. В общем не впечатлила.
Смотрел Titan, там тоже все очень сложно, особенно с индексами.
Встречал отзывы, что RDF-базы на сложных запросах могут обгонять реляционные.
По скорости virtuoso очень хорош, к тому же поддерживает и реляционный SQL. Правда, поддержка OWL в нем очень ограниченная.
По-моему, недавно 2ГИС делал статью про Neo4j и крайне нахваливал ее в плане производительности на графах. Лень искать, но если интересуетесь — посмотрите в их корпоративном блоге, буквально пару месяцев назад.
ОриентДБ используем и собираемся разворачивать в продакшене.

Идея отличная, реализация — сыровато местами.

Но активно растет и набирает скорость (надеюсь будет набирать еще увереннее, не хватает 2-3 звезд в сообществе, для кода и пропаганды)
Т.е. сыроватость тут следствие и даже хороший признак.
По моему вопрос не в том хороша или плоха MongoDB? И да же не в том права ли Сара? А в том, что в серьезном проекте вряд ли обойтись без транзакций и реляционных данных (финансы). А те фичи что дает MongoDB, сейчас во многом доступны например в PostgreSQL, даже MySQL пытается предложить похожий функционал.
Вопрос в том, стоит ли заводить вторую БД (MongoDB, etc) для нереляционных фишек? Если бы это давало прирост производительности в разы — то да, если нет — то нет :)

Я думаю очень ожидаема и интересная статья сравнивающая нереляционные фишки PostgreSQL с MongoDB. Во всяком случае это будет действительно интересно для тех, кто сейчас выбирает платформу под проект. А права ли Сара? NBC.
Мне кажется, нечестно приводить идельный пример для монги (с известными недостатками).
Мы использовали монгу для поиска по геоданным + некоторые другие данные (около 20 полей), так вот оптимизация запросов к такой базе не такое простое дело, к тому же жрет много места.
По своему опыту могу сказать, что графовые базы, несмотря на их название, отлично работают с графами (в том числе, с социальными графами). Конечно, не стоит брать графовые базы в качестве основного хранилища данных.

И почему же не стоит брать графовые субд, которые отлично работают с графами, в качестве основного хранилища графовых данных? Чтобы жизнь раем не казалась? :-)
Возьмём в качестве примера модель пользователей. Когда будет удобно и эффективно использование графовых баз? Тогда, когда нам надо получить список общих друзей между пользователем А и Б. Или количество «рукопожатий» от пользователя А до пользователя Б. А если нам надо найти пользователя по имени, то здесь графовая база будет уже мягко говоря не эффективна. Вы же не используете sphinx в качестве основного хранилища для полнотекстовых данных? Вы используете его только для полнотекстового поиска. Более подробно про дружбу графовых баз можно почитать здесь (на примере дружбы MySQL и Neo4j).
А если нам надо найти пользователя по имени, то здесь графовая база будет уже мягко говоря не эффективна.
Создаётся текстовый индекс по имени. Какие проблемы?
Согласен, с поиск по имени — это неудачный пример. Попробую привести другой. Например, работа со связанными данными: получить список пользовательских постов. Или фотографий, разложенных по альбомам. В принципе это конечно можно как-то организовать и в графовой базе, но сами понимаете на сколько это будет неэффективно.
Дело в том, что в графовых это организуется самым естественным образом — берете альбом и находите все связи, ведущие от него к фотографиям. И это будет очень эффективно, так как поиск производится среди небольшого количества связей единственного альбома, а не наоборот, когда миллион фотографий фильтруются в поисках ссылки на один альбом.

Все это — вопрос ограничений по задаче, а потом уже — вопрос подхода и организации, и реализации, и популяризации, и… (… ции)

У вас очень наивное представление о работе движков СУБД. В РСУБД любая операция поиска ускоряется индексом. По сути запрос к таблице фотографий с выборкой по альбому не будет бегать по всей таблице, а выберет только нужные записи из индекса, с количеством операция чтения равным логарифму от общего числа записей в таблице фотографий по очень большому основанию. На миллионы записей фотографий будет менее 10 операций чтения.

Если же у вас графовая БД, то вам надо сначала найти узел альбома по ключу, а это потребует той же логарифмической сложности операций. Потом получить ссылки на фотографии и выбрать данные по фотографиям. Вот тут кроется самая большая жопа. Потому что поиск фотографий по ключу тоже логарифмическую сложность имеет.
Варианта решения два:
1) индексы с включенными колонками в РСУБД, которые хранят связанные данные рядом.
2) Хранение связанных объектов вместе с родительским — подход документарных СУБД.
А вот для графовых субд хорошего решения нет.

Все это можно соптимизировать, если данные целиком влезают в память. Тогда вместо древообразных индексов можно использовать хэш-таблицы с константным временем поиска. Собственно такой подход легко реализовать на MS SQL\Oracle с использованием inmemory движков. А MS SQL еще и код процедур, работающих с такими данными, умеет компилировать в нативный код.
Спасибо за ликбез ))

Вы описали работу, которую необходимо выполнить для эмуляции графов в реляционной базе.

Нет никакой эмуляции графов, потому что графов нет. Получение списка строк по значению внешнего ключа к графам никакого отношения не имеет.

Графовые операции — это транзитивное замыкание и поиск пути, но на практике нечасто встречаются. Даже там, где исходные данные представляют из себя граф.
Вообще говоря, граф — это куда более естественное представление данных, чем таблички.
Это лишь точка зрения, не имеющая никакого практического отражения.
В исходной статье про было написано.

Мне очень нравится Ваша категоричность.
Очень нравится Ваше знание специфических технологий (в других местах вы назовете их костылями, видимо) «больших» РСУБД.

В начале 90-х я бы взял Вас на работу не задумываясь. Мне тогда, также как vintage сейчас, приходилось объяснять, что такое реляционные отношения, почему это работает и почему мы обязательно перейдем на SQL — вот Вам бы и поручил.
К сожалению сейчас я с категоричными людьми не работаю.

Но я обязательно запомню фразу
Нет никакой эмуляции графов, потому что графов нет.

Спасибо ;)
А вы считаете что получение фотографий альбома имеет какое-то отношение к графам?
)) судя по минусу я Вас обидел. прошу прощения

у меня встречные два с половиной вопроса — какого альбома; к каким графам; а Вы считаете, что нет?

но да. я считаю, что фотографии — в альбомах — данного человека — это граф.
ну и естественно — дерево, если фотографии материальны, а альбомы не могут принадлежать двум людям сразу.
И что? Нужно применять алгоритмы работы с графами? Надо иметь структуры данных описывающих графы?
Нет, не нужно. Любое применение теории графов к задаче показа фотографии в альбомах приведет только к усложнению.

я считаю, что фотографии — в альбомах — данного человека — это граф


Это лишь мнение, которое никакого практического отражения не имеет.
Слушайте, кому нужно? Кому не нужно?

Все зависит от задачи, а не от Вас и Вашего категорического отрицания чужих непрактичных мнений.

Извините, но Вы не поняли — предыдущим письмом я ничего объяснять и не пытался. Это была ироничная попытка закончить разговор и про дерево.
Тогда что значит фраза:

я считаю, что фотографии — в альбомах — данного человека — это граф


?

Мы же вроде в техническом сообществе, где надо мнение подкреплять фактами, а не просто верой.
Подумал и решил, что нужно, все-таки, попытаться объяснить, в последний раз.

Во-первых, конечно, во всех задачах, где требовалась иерархия и гибкая структура документа предыдущие 15 лет (ну так случилось) я использовал RDBMS (ну так приходилось). И все плюсы и минусы такого подхода хорошо известны не только мне.

Во-вторых, все понимают, что Вы большой специалист по MSSQL. Честно. И Вы можете считать, что нужно обязательно использовать все тонкие и новые ее особенности. Но, главное — поймите же Вы, наконец:

Во-первых — _все зависит от задачи_.

Во-вторых — чужое мнение может иметь основание и «практическое отражение» (Вы так часто утверждаете обратное, что хочется посвятить этому отдельный пункт)

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

То что я специалист по MS SQL ничего не меняет. Я вам писал только о том, что реализовано в большинстве (если не во всех) РСУБД (индексы и btree). Новые возможности позволяют покрыть другие классы задач, но к текущей задаче (см выше) отношения не имеют. Привел я их исключительно чтобы вы понимали, что прогресс РСУБД не остановился во времена когда Кодд придумал реляционную алгебру.

А если ваше мнение (список фотографий) имеет объективное обоснование — напишите его. Я не вижу объективных оснований применять знания теории графов и алгоритмы на графах в текущей задаче. И не забывайте, что мы говорим о конкретной задаче — фотографии в альбоме.

Привел я их исключительно чтобы вы понимали, что прогресс РСУБД не остановился во времена когда Кодд придумал реляционную алгебру.

Буду считать, что Вы шутите. Во второй раз.

Давайте я Вам объясню. Тут никто не спорит с Вами о том, что SQL хорошо и реляционные базы — лучше.

Мы тут говорим, что и другие подходы имеют место быть. По задаче.
Как пример — топик посвящен МонгоДБ, «документной» базе, которую я никогда не использовал и, надеюсь, не скоро буду (дело не в ней, кстати — надеюсь, просто, что такие простые задачи, для которых она эффективна отвлекать, пока, не будут). Но кто-то — будет. И в Вашем переводе — не говорится о том, что Монго плохая сама по себе, это только в мультфильмах бывают персонажы с титулом «мистер Зло» (умные, но идиоты, обычно). Речь о том, что нужно думать и выбирать не по «вере», а по ограничениям.
Надеюсь Вы понимаете, что никто реляционные базы данных у Вас (и у нас) отбирать не пытается. Пусть расцветут, как говорил…

Ну так вернемся. Вот у Монго для некоторых задач есть преимущество, по сравнению с Оракл 12С, например. А тех, кто будет говорить, что она подходит для всего и ответит за все — гоните.

Какие преимущества? Поставить по инструкции не сложно. Настраивать не нужно. Лишних хитростей нет. Простому сайту — простая база. Ведь замечательно! Возможно вы сможете меня убедить, что для современного «социального блога» нужна RDBMS, по лицензии на ядро… Но зачем?

Я не вижу объективных оснований применять знания теории графов и алгоритмы на графах в текущей задаче.

Вы про что, кстати? Про задачу Коммивояжера? Или про фракталы?
Или, все-таки, про способ выражения отношения множеств через графы?
Про индексы и btree, про которые Вы, оказывается, пытались мне объяснять (!) (спасибо!).
Не понимаю.
Я так понимаю статистические базы данных должны применять в своей работе исключительно статистику, а геоинформационные — геоинформацию?

Я знаю, звучит бредово. Мы же тут про «графовые базы».

Но давайте будем конкретными. Вот у OrientDB — одно отношение к «graph database», у Фейсбука другое, у Гугла третье. Почитайте, врага надо знать в лицо. Кстати, если прочитав про Фейсбук вы вскрикнете «ага, в MySQL же персистентность» — лучше Вам мое лицо не видеть.

Кстати, почитайте, почему в IBM так долго тянули с публикацией DB2, и если Вы специалист, вспомните, что тот же DB2 оказалась фактически первой _приемлимой_ современной «object relational» БД.

И не забывайте, что мы говорим о конкретной задаче — фотографии в альбоме.

Да. Я надеюсь уже понятно, что пинать «графовые базы», это как пытаться проткнуть туман.
Поэтому давайте про конкретное, снова.
Почему для больших и богатых «графовый» подход пригодился мы там наверху почитали.
Теперь, если мы говорим про тот же ОриентДБ — вот вам приблизительное описание задачи:

Мы делаем сервис хранения фотографий для жителей небольшой страны с населением в 6 миллионов. Самые частые обращения — пользователя на запись — к своим альбомам, в которых лежат свои фотографии. Иногда пользователь может «отправить» фотографию другу или родственнику и тогда она тоже будет «лежать» у него в альбоме. Страна, хоть и редконаселенная, но по размерам почти соответствует Германии, Австрии и Венгрии вместе взятым. Связь между населенными пунктами не самая быстрая, каналы загружены, интернет популярен исключительно мобильный.

Альбомы у нас хитрые — вкладываются друг в друга. Ну как папки «виндовса». Почему? Потому что пользователю так удобно. Сортировать. Понятно, что структура папок и глубина вложенности у каждого своя. Фотки у нас электронные, так что написать им на обратной стороне разные данные — не проблема. Какие? А какие попросит. Мы ему даже подскажем, семантика у нас — ого-го. И растет по запросам и классам доменов (даты; возраст ребенка, в годах; стоимость блюда на фото или, даже, расстояние в метрах от дома).

Люди в этой стране устроены так, что живут, в основном, большими семьями. Три колена в соседних домах одной деревни. Фоток у них много, особенно со свадеб. Ну и машины свои любят, очень.
Еще, как следствие — плевать им на селфи хаброводов и неизвестных виртуальных друзей, но родственник для них — это не только мама с папой, но еще и восемнадцать троюродных дядь, у каждого из которых не меньше трех сыновей и дочерей столько, сколько нужно, чтобы три сына накопилось (но минимум три девочки, иначе кто за сопляками присматривать будет?). Понятное дело, что в первую очередь они хотят видеть 400 фоток очередной свадьбы каждого ближайшего родственника в обратной пропорциональности к степени убывания родства, а во вторую — прямопропорционально «весу» родственника в обществе.

Сервис, кстати, используется не просто так, как Вы понимаете, а многофункционально. Запросы типа покажи мне все фото детей дочерей брата бабушки по маме — достаточно частые и формироваться могут произвольно, практически на человеческом языке (язык в той стране, благо, подходящий).

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

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


Знаю, Вы ждали от меня Теорий, мне они _тут_ не интересны, лучше порыться в SIGMOD имени Кодда, пожалуй. Зато ТЗ можете расписать по этому брифу сами.

Но суть проста. На выбор решения влияет не только Ваше объективное мнение, но и чужие, непрактичные ограничения. А иногда и простое желание двигать что-нибудь дальше.
<spoiler title="Заголовок">Содержимое</spoiler>
    Вставка спойлера (разворачиваемый блок информации). 
Вставка спойлера (разворачиваемый блок информации).

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

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

Это вранье. Без десятков настроек, нетривиального разворачивания кластеров и прочих «радостей» монга не взлетит в сколь-нибудь серьезном проекте. По сравнению с монгой поставить MS SQL Express\Localdb или postgres не стоит вообще ничего и настроек не надо.
Вы банально введётесь на маркетинг, как и тысячи других людей, в том числе тех, кто пытались пилить социальные сети на монге.

Кстати, если прочитав про Фейсбук вы вскрикнете «ага, в MySQL же персистентность» — лучше Вам мое лицо не видеть.

Снова стандартная маркетинговая разводка. Как только исключаем из рассмотрения чтение с диска и запись на диск, то резко переходим из систем класса «СУБД» к системам класса «кэш». Там совершенно другие «правила игры», можно применять множество алгоритмов, которые не доступны при хранении данных на дисках. Но несмотря на все возможности кеша данных-то сильно больше чем ОП, и мы снова упираемся в диск. При таком раскладе преимущества большинства NoSQL крайне туманны.

Про фотки кейс красивый, теперь жду практической реализации. А то на словах всегда круто, а на деле очередное говно получается.
да… как-то печально все… ну ок.

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

но себе Вы слух изрядно портите.

У меня со слухом все хорошо, и со зрением тоже. Но главное что я могу еще головой думать и фильтровать то что вижу и слышу. Чего и вам советую.
Понятное дело, что в первую очередь они хотят видеть 400 фоток очередной свадьбы каждого ближайшего родственника в обратной пропорциональности к степени убывания родства, а во вторую — прямопропорционально «весу» родственника в обществе.


А запрос-то несложный получается.

select top 400 ...
from photos p
join tagphoto tp on p.objectid = tp.objectid
join tags t on tp.tagid = t.id
join relations r on p.owner = r.target
join users u on u.id = r.target
where r.source = @userid
    and t.name='свадьба'
    and r.depth < 6
order by r.depth, p.date, u.score


Так как родственные связи меняются нечасто, то просто храним пути в дереве для каждого пользователя, «вес» в обществе — можно пересчитывать раз в день, а можно просто материализованным представлением сделать.
ну, слава Богу.

я уж испугался, что чувство юмора действительно не всем дано…

вес в обществе лучше выражать через материализованное представление. однозначно.
Ну а теперь реализацию на графовой БД в студию.
Примерно так:

SELECT expand( tag['свадьба'].out( "photo" ) )
FROM (
  SELECT
  FROM (
    TRAVERSE both( "friend" )
    FROM (
      SELECT
      FROM Person
      WHERE id = '123'
    )
    WHILE $depth < 6
  )
  ORDER BY $depth ASC , score DESC
)
LIMIT 400
Если запрос выполняется ровно так, как написано, то он получит всех друзей до 5 колена, это будет нереально большое число. Есть теория 6 рукопожатий, которая говорит, что между любыми двумя людьми есть цепочка из 6 знакомых. Ограничив обход 5 уровнями получим 80% всех person или более.
Так что без индексов никуда.

РСУБД не делает индексы по рекурсивным запросам, зато можно пересчитывать в момент добавления\удаления связи. Это случается сильно реже, чем просмотр фотографий. А дальше уже из по индексу выборка пойдет очень быстро.

А смысл тогда вообще в ограничении в 6 рукопожатий? :-) Но но вы правы, в данном случае нужно использовать поиск в ширину, тогда не нужно будет выбирать всех друзей:

SELECT expand( tag['свадьба'].out( "photo" ) )
FROM (
  SELECT
  FROM (
    TRAVERSE both( "friend" )
    FROM (
      SELECT
      FROM Person
      WHERE id = '123'
    )
    WHILE $depth < 6
    STRATEGY BREADTH_FIRST
  )
  ORDER BY $depth ASC , score DESC
)
LIMIT 400
Ой-ой. Как же я пропустил-то все… вот только под утро субботы и добрался… спасибо, vintage!

Должен признаться (карму мне понижать Вы уже не сможете, так что не страшно ;))) — я уже много лет не занимаюсь непосредственно программированием, но, навскидку — запрос может быть, например, таким:

TRAVERSE wedding_album.out_photos FROM (
   SELECT FROM (
      TRAVERSE both(has_relative) FROM @userRID STRATEGY BREADTH_FIRST
   ) ORDER BY $score DESC, $depth ASC  
)
LIMIT 400

Надо попросить ребят в понедельник проверить… :).
Есть, правда, один плюс — то, что я написал — выглядит правильно и, скорее всего, работает правильно. Если нет — можно обратиться к разработчикам и, почти уверен — если они со мной согласятся — то так и сделают (правильно). Хотя это отступление лишнее.

Есть и минус — мне не нравится костыль с «селектом». Но он тут только из-за того, что не нашел сразу в документации сортировки для траверса (и, кстати, понимаю почему, так что долго и не искал).
Но, заметьте, вес мы проверяем у связи (т.е. важность конкретно для пользователя), а не у человека вообще (это что за алгоритм такой, который вычислит вселенскую важность каждого индивидуума?).

Теперь по поводу Вашего кода — я молчу про r.depth<6, это частности, может быть и мне стоило указать (не думаю, думаю даже, что SORT BY $depth ASC — лишнее тут), но согласитесь, Вам пришлось сделать очень важные допущения.

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

Но дальше — Вы допустили, что связи будут только родственные. Меняются редко и поэтому вы можете для каждого человека перечислить все его «колена», указав просто глубину удаленности каждого (но, если это родственники, — это мы повторяем одну и ту же структуру для каждого!). Хорошо, пусть.

Хотя почему. Я ведь не говорил, что связи могут быть ТОЛЬКО родственными. Задача-то выдуманная, но вот реальность подсказывает, что с большой вероятностью интереснее окажется следить за фотками коллег. С чем к нам заказчик (уверен) и придет завтра, сияя глазами.

ну еще многое из задачи пропущено. не принял бы я такую систему, когда не уточняя пишут «правильно» и еще предъявить что-то просят.

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

Код можете не писать. Я знаю как это cделать и в реляционной эмуляции графов и вот в частном варианте того-же ОДБ.

ох. устал писать.
хочу только сказать, что вот мой запрос, после всех замечаний — переписывать не придется совсем… ну и еще много чего хочу сказать, но туннельный синдром замучал )

а главная мысль, еще раз — в заголовке этой статьи. Согласитесь уже, сэкономьте нам время.
Любой индекс — это дерево, которое ВНЕЗАПНО является частным случаем графа. Иными словами, в графовой базе практически не нужно заводить отдельные индексы, потому, что графовая база — сама по себе уже индекс.

Рекомендую посмотреть эту презентацию.
Вы путаете форму и содержание. Причем настолько, что ваше выражение превращается в полную чушь.

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

Причем второе верно только для B+ деревьев, все остальные виды деревьев таким свойством не обладают. А уже графы не обладают ни первым, ни вторым.

Поэтому если графи сохраняется на диске, то для него также нужен будет индекс, причем в виде B+-дерева. А если граф весь влезает в память, то можно иметь прямые ссылки в памяти между записями, но их сложно сохранять во внешнем хранилище. Поэтому используют более простой вариант — хэш-таблицы.

Те же самые хэш-таблицы можно использовать и в inmemory движках в РСУБД.

По ссылке презентацию посмотрел, там есть ошибочное предположение, на котором построена вся идея, что нужно делать джоины по сотням связей. В реальном приложении обычно не нужно, а если нужно, то такие джоины денормализуют.
Поэтому если графи сохраняется на диске, то для него также нужен будет индекс, причем в виде B+-дерева.
Почему? Ведь B+-дерево мы храним на диске, не используя никакие дополнительные средства, почему для произвольного графа без них будет не обойтись?
Потому что пока не придумано способа хранить данные на диске с возможностью поиска без использования B+ дерева (вернее придумано, но они менее эффективны).

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

Я вот вижу более-менее эффективный способ — иметь два индекса для ребер по узлам на обоих концах. Это полностью эквивалентно таблице ребер в РСУБД с теми же двумя индексами. И с теми же проблемами при обходе (traversal).

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

А если ограничить количество связей, по которым будет строиться такой индекс, то окажется что это решение эквивалентно индексированным\материализованным представлениям в РСУБД.
Давайте какую-то совсем упрощенную модель попробую накидать.
Много букв
Предположим, у нас все вершины и все ребра одного типа, то есть для всех вершин храним какой-то одинаковый набор данных и для ребер тоже какой-то другой свой набор.

Данные о вершинах храним в одном гигантском бинарном файле. Тогда физический адрес вершины — это смещение от начала файла для её блока данных. Если вершину необходимо удалить, то, конечно, мы ничего не смещаем в нашем файле, а просто этот блок данных становится свободным, и в последующем мы запишем в него данные о новых вершинах. Чтобы хранить данные о свободных блоках, прям на них же построим односвязный список (прием, аналогичный тому, что используют аллокаторы памяти). Если свободных блоков нету, то дописываем в конец файла. Так как мы схитрили и сказали, что все вершины одинакового типа (и следовательно, записи об их данных одинакового размера), то сильно страдать от наличия неиспользуемой памяти в середине файла мы будем только в маловероятном сценарии, когда мы добавлять вершины перестали, а начали их только удалять в промышленных масштабах. Как нетрудно заметить, физический адрес вершины при такой схеме никогда не меняется.

С ребрами ровно та же система (один большой файл на всех), для ребра храним его свойства и физические адреса вершин, которые он соединяет, физические адреса ребер тоже никогда не меняются. Также для каждой вершины храним список физических адресов ребер, из неё выходящих. Хранить все эти списки будем в третьем гигантском файле. Каждый отдельный такой список представляет из себя односвязный список блоков фиксированного размера (такая структура называется chunked vector): то есть, например, 20 адресов ребер и указатель на следующий такой блок из 20 ребер. Поскольку у нас снова всего блоки одинакового размера, применяем все ту же технику хранения. Дополнительно для каждой вершины храним физический адрес самого нового (ещё не до конца заполненного блока), с которого, переходя по указателям, можем прочитать весь список ребер (за время, пропорциональное его длине). Для ребра во втором файле храним указатели, по которым оно записаны в обоих списках вершин. Добавление выполняется за константу, удалением чуть более трудоемко (для каждой вершины смотрим самую последнюю добавленную позицию в списке, меняем её местами с удаляемой из этого списка, а потом уже все удаляем), но тоже за константу.

Если хотим, можем отдавать наружу прямо наши смещения, если нет, то запилить отдельные B-деревья, которые будут по ключу выдавать смещение. В такой реализации мы не умеем быстро отвечать на вопрос, соединены ли две вершины между собой ребром, можем для этого запилить большой индекс на всех парах соединенных между собой вершин, можем подумать как модернизировать наш chunked vector (например, превращать его в дерево поиска или хеш таблицу, если ребер становится больше некоторой константы), в общем, варианты есть.
Ну ок, а теперь решите задачу получения списка фотографий в альбоме по заданному ID альбома
1) Как получить адрес по ID?
2) Как получить список ребер?
3) Как отфильтровать ребра нужного типа?
4) Сколько чтений с диска для этого понадобится (считаем что каждое обращение к диску вызывает реальное чтение)?

После подсчетов окажется, что РСУБД эффективнее.
По ссылке написано про запись, а про чтение даже примера кода нет, не то что замеров.
Внимательно перечитайте, пожалуйста :-)
Дважды перечитал, не увидел ни замеров, ни примеров кода. Код в статье занимается записью данных.
Зачем нужен индекс для поиска записи по физическому адресу?

Я прекрасно знаком с реальными приложениями, где применяется денормализация (у нас сложная графовая структура реализована, как раз на постгресе). Куча дублирования данных, куча индексов, куча лишиних таблиц, Массовые операции длящиеся по минуте, там где, в нормализованном графе достаточно изменить пару связей.
Потому что клиент не пользуется физическим адресом записи, а пользуется ключами.
1. Клиент таки может ими пользоваться
2. Для траверсинга по графу чем пользуется клиент совершенно не важно
1) То есть индекс по ключу таки нужен
2) Зато важно как оно хранится.

Возьмем сценарий фотоальбомов и примитивный вариант хранения
1) Предположим вы по ID нашли RID (физический адрес) альбома.
2) Далее вам нужно получить ребра типа «вложен», как получить эти ребра? Предположим вы храните RID ребер в самой вершине.
3) Вы читаете N ребер, которые скорее всего вызывают N чтений с диска, но нужно вам K и K<N, потому что не все ребра имеют тип «вложен».
4) По K ребер вы получаете K вершин, что вызывает еще K чтений.
Общее количество чтений (N+K+key lookup). При этом чем глубже обход, тем хуже ситуация.

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

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

Поэтому сценарий с хранением ключей в индексе требует меньше записей при изменении, то утяжеляет собственно обход. Если обход ограничен по глубине — можно сделать индекс, который аналогичен materialized view.

Для сравнения тот же сценарий в РСУБД:
1) У вас есть ID альбома и вам больше ничего не нужно для поиска фотографий
2) Вы делаете запрос к индексу, который отдает данные за количество чтений, сравнимое с key lookup в предыдущем сценарии.
1. Зачем?
2. Разумеется.

В OrientDB есть два типа рёбер:
1. легковесные, которые не имеют свойств — они хранятся как взаимные ссылки между узлами
2. полноценные, которые имеют свойства и которые хранятся отдельными записями.

Вы сейчас описали работу второго типа рёбер. Только в OrientDB N будет равно K, потому, что рёбра группируются по типу, а не лежат все вперемешку. Получаем число чтений 2*K + 1. С первым типом рёбер будет K + 1. В обоих случаях сложность одинаковая O(K).

А вот, что будет, если нам надо не тривиальный запрос сделать, а подтянуть связанные данные:

Легенда:
* K — число интересных записей в первой таблице
* M — число связанных записей во второй таблице
* T — общее число записей в базе

РСУБД:
* выбрать записи из первой таблицы — O(K)
* найти связанные записи по индексу — O(K*log(T))
* выбрать записи из второй таблицы — O(M)
Итого: O( K*log(T) + M )

Граф:
* выбрать записи из первой таблицы — O(K)
* выбрать записи из второй таблицы — O(M)
Итого: O( K + M )

Да, при перемещении записи будут обновлены все записи ссылающиеся на данную. Точно также как нужно проапдейтить все индексы, в РСУБД.

> У вас есть ID альбома и вам больше ничего не нужно для поиска фотографий
Ага, а права доступа кто проверять будет?

> Вы делаете запрос к индексу, который отдает данные за количество чтений, сравнимое с key lookup в предыдущем сценарии.
Хранить данные в индексе — это очень идеальный случай, оторванный от жизни. В реальности оказывается, что ключей на разные запросы нужно много и данных тоже надо много. А раздутый индекс не такой быстрый как маленький, да и в память уже не влезает.
1) Физические адрес могут меняться. Тупо добавится поле и придется адреса двигать, поэтому во внешнюю систему нужно отдавать ключи.
2) Ваши расчеты не верны.

Если мы выбираем по ключу, то в РСУБД вообще не надо ничего из первой таблицы выбирать.
Если мы выбираем не по ключу, то надо будет в графе просканировать все записи в первой таблице.

Для РСУБД будет O(K*log(T)), потому что можно построить покрывающий индекс и к второй таблице обращений не будет. При этом логарифм по очень высокой степени. Для всех мыслимых размеров таблиц log(T) будет меньше 10.

Для графов будет M случайных чтений из второй таблицы, для РСУБД будет K*log(T) линейных чтений из индекса (и будем считать что индекс пересобираем по ночам и он не успевает испортиться). Случайное чтение будет в 3-10 раз более дорогим, чем линейное. Поэтому коэффициент при M в графах будет сильно больше, чем при K*log(T) в СУБД.

Индексы в РСУБД содержат ключи, а не RID если есть кластерный индексы на таблице, это значит, что при pagesplit индекс не надо править.
В графах править ссылки придется очень часто, потому что вершины будут постоянно перемещаться при добавлении ссылок в вершины. Это приведет к тому, что при записи придется очень много вершин трогать.

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

Ну и самая главаня фишка, что в РСУБД можно сделать индексированное представление, которое при чтении дает линейную сложность, независимо от количества таблиц, участвующих в формировании.
Вы на какие-то странные вещи в своих подсчетах упираете. Говорите, что можете положить в индекс вместо ссылок сами данные, что вам мешает сделать тоже самое для списка ребер (можно создать отдельный список, положив в него только ребра нужного типа)? Упираете на то, что читаете с диска большими блоками, сделайте так, чтобы пока у вас список ребер меньше этого размера блока, он лежал бы одним последовательным куском, а потом несколькими кусками с размером в блок. Можно же использовать для реализации списка ребер практически любую структуру данных, причем можно даже гибко переключать их: если ребер мало — использовать одну, если много — другую.
А я говорю, что так нельзя? Даже наоборот, я сказал бы что так нужно делать. Обсуждение родилось как раз из фразы vintage:
в графовой базе практически не нужно заводить отдельные индексы, потому, что графовая база — сама по себе уже индекс


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

Кстати индексы вовсе необязательно прописывать руками. RavenDB например имеет автоиндексы. Но тут многое зависит от архитектуры СУБД в общем, а не только от хранения.

А если мы начинаем везде использовать индексы, то нам становится не так важно как физически база хранит данные, гораздо важнее становится удобство управления.
А если мы начинаем везде использовать индексы
Но мы не используем здесь индекс (структуру данных, по которой можем быстро искать) для нашего графа, а просто заимствуем ваш прием и храним нужные данные прямо в списке ребер. Для данной задачи (вывести все вершины, соединенные с заданной) нам индекс не нужен, достаточно любой структуры, которая может выдать все хранящиеся в ней элементы.
Действительно индекс не нужен, если данные из себя представляют индекс. Но это мы рассматривает один запрос. На практике будет много разных запросов и не получится сделать одну структуру для любых запросов.
Ага, а права доступа кто проверять будет?
Это не зависит от способа хранения, предлагаю не примешивать в обсуждение нерелевантные вещи.

Хранить данные в индексе — это очень идеальный случай, оторванный от жизни
На парктие это как раз единственный жизнеспособный сценарий. Все нетривиальные (не по ключу) выборки должны иметь покрывающие индексы. И я очень редко видел когда этого нельзя добиться. В памяти индексу влезать не обязательно, он использует те же b-деревья и очень быстро читается с диска даже для очень большого количества строк.

Даже в случае с графовыми структурами можно этого добиться, не говоря уже о деревьях или обычных связях.
Зачем кучи таблиц для графов? Достаточно таблицы вершин и ребер, все остальное через materialized view.
Потому что связываются узлы разных типов, которые находятся в разных таблицах: Задачи, папки, пользователи, группы пользователей, аккаунты, права и прочее.
И что? Дайте уникальный ключ каждой записи (guid) и сделайте одну таблицу связей-ребер. Для распространенных выборок сделайте материализованные представления и обновляйте их асинхронно.

Главное не делайте guid кластерным ключом.
Ну то есть реализовать графовую БД в рамках одной гигантской таблицы РСУБД, но с крайне дорогим траверсом (при нормализации) или крайне дорогими апдейтами (при денормализации).
Ну да, в любых системах хранения так — или быстро читаешь или быстро пишешь. Одновременно быстро читать и писать можно только при снижении надежности и\или конкурентности.

Фактически графовая бд, храня ссылки\ребра в вершинах сильно увеличивает, стоимость записи. Можете сделать тоже самое с материализованным представлением.

А можно нубский вопрос? Почему нельзя сделать так, чтобы переход по индексу не требовал его поиска? То есть индексы у нас лежат в каком-то дереве поиска, мы в нем ищем ключ и на выходе получаем некоторый физический идентификатор, по которому наши данные лежат (не знаю точно, что он из себя представляет, скажем имя файла+позиция от начала). Почему нельзя вместе с индексом положить сразу этот физический идентификатор? Будут проблемы, когда у записи этот идентификатор изменится и нам придется его везде обновлять? Тогда из-за чего такое обновление будет происходить и нельзя ли от этого как-то избавиться?
Индексы как раз этим и занимаются — переводят «значение ключа» в «физические координаты». Физические координаты — это грубо говоря и есть «имя файла + смещение». Поскольку РСУБД не допускают хранения физических координат в качестве значений, то каждый переход по отношениям требует задействования какого-либо индекса (либо перебор всех записей, если подходящего индекса не нашлось). Графовые базы (не скажу за всех, но OrientDB точно) хранят связи сразу как «физические координаты» с соответствующим константным временем перехода по отношениям.

Но физические координаты могут меняться, да. Например, когда происходит ребалансинг между кластерами. Поэтому вовне физические координаты лучше не отдавать.
Да, спасибо, я не видел ваш комментарий с презентацией, когда писал свой, и задавал вопрос автору комментария уровнем выше. А что мешает в РСУБД такой же подход использовать? В смысле, что мешает допустить это самое хранение?
Тут мало просто хранить надо ещё правильно с этим оперировать. После решения всех проблем, получится уже не РСУБД, а графовая база данных :-) Фактически, тот же OrientDB — это на самом деле документоориентированная база с прямыми физическими ссылками (и траверсинга по ним), опциональной схемой и поддержкой ACID.
Можно и гораздо проще. В индексе сохраняются сами данные, необходимые для выполнения запроса. INCLUDED COLUMNS называется. Тогда одним запросом к индексу вытягиваются сами данные, а не ссылки на записи. Такой индекс называется покрывающим и крайне рекомендуется иметь покрывающие индексы на запросы, которые тянут не все поля из таблиц.

Но предположим вариант что у нас нет покрывающего индекса и в индексе просто ссылка на физическое расположение данных, например смещение от начала файла. Но все равно остается необходимость поиска данных по ключу, поэтому сами данные будут хранится примерно также — в древообразной структуре. Но это означает, что периодически надо будет структуру дерева менять (page split для B+, балансирование для AVL и RB-tree итп), это значит что физическое смещение записи в файле данных будет меняться.

Когда структура поменяется надо будет найти в индексе записи, ссылающиеся на старые адреса и обновить их. А как найти такие адреса? Только обходом всего индекса. Это будет непозволительно долго, поэтому индексы хранят не адреса, а ссылки (ключи) на записи.
Но все равно остается необходимость поиска данных по ключу, поэтому сами данные будут хранится примерно также — в древообразной структуре. Но это означает, что периодически надо будет структуру дерева менять (page split для B+, балансирование для AVL и RB-tree итп), это значит что физическое смещение записи в файле данных будет меняться.
Почему? Будем в индексе поиска по ключу хранить не сами данные, а ссылки на их физическое расположение, тогда ребалансировка не будет изменять расположение данных.
Когда структура поменяется надо будет найти в индексе записи, ссылающиеся на старые адреса и обновить их. А как найти такие адреса? Только обходом всего индекса.
Ну не всего, если у нас граф, скажем, неориентированный, то мы знаем с какими вершинами наша связана, ровно для них и надо менять. Но, вообще, надо стремиться, чтобы данные своё физического расположения не меняли.
Будем в индексе поиска по ключу хранить не сами данные, а ссылки на их физическое расположение, тогда ребалансировка не будет изменять расположение данных.


Можно и так, например так делает MS SQL для HEAP таблиц (для которых нет кластерного индекса).
Но это создает больше чтений при получении по ключу, что особенно сильно бьёт по производительности при джоине.

Ну не всего, если у нас граф, скажем, неориентированный, то мы знаем с какими вершинами наша связана, ровно для них и надо менять.

В графовых БД будет сильно зависеть от того, как индексы построены. Если будет индекс обхода, то его придется менять почти весь.

Но, вообще, надо стремиться, чтобы данные своё физического расположения не меняли.

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

На самом деле B+ деревья и постоянно возрастающий ключ как раз позволяют уменьшить количество перестроений дерева до нуля (поэтому рекомендуется int identity в качестве кластерного ключа в РСУБД).
Хм, сдаюсь. Когда я пару лет назад работал с Neo4j (правда ещё первой версии), практически уверен, что видел прямо у них в доках рекомендацию не использовать её в качестве основного хранилища. Видимо, много воды утекло с тех пор. Что ж, это отлично, что они так быстро ушли вперёд.
Тут вопрос зрелости продукта, скорее, чем применимость технологии.

Neo4j молодцы — популяризаторы.

Такие базы здесь давно (из известных — посмотрите историю иерархической InterSystems Caché, или объектной GemStone) и работают напряженно (почитайте, как Гугл тихо переходит на Pregel).

Дело только в том, что графы бывают разными, подходы бывают разными ну и в решениях (пока) больше уникальности (иерархические, объектные, RDF, да что угодно еще), по сравнению с одной реляционной математикой Кодда (но и IBM варил DB/2 20 лет, пока решился выстрелить).

Хотя есть Tinkerpop/Blueprints, плюс задачи современные чаще всего подводят новых изобретателей со стороны гибридных key/value-документно-графовых «находок». Спрос на такие документно-графовые базы будет расти, я уверен. Все-таки объектные отношения в них эмулировать гораздо проще и, зачастую, эфеективнее. Т.е. это просто следущий шаг для освоивших Монго, etc. Включая все модные термины современного популярного веба типа схемалесс, масштабируемость и пр.

Мы поставили на ОриентДБ, надо им помочь «возмужать», конечно — но главное — нашей задаче эта модель подходит, все.
Взвешивание кучи параметров привело к шаткому перевесу в сторону такого рискованного пути и главное для баланса — двигаться.
В нашей команде исторически сложилось использование MongoDB. Как сисадмин плююсь, матерюсь, ругаюсь, но жру кактус. Даже программисты node.js не всегда рады монге.
Хотя, возможно, может в rpm-based она и лучше работает, не проверяли.

P.S. Хотя когда всё работает так, как ожидается, монга вполне себе шустрый и удобный(javascript) инструмент.
Если честно ваша ответная статья получилась значительно менее убедительной, чем изначальная. Да, ребята из Diaspora сильно облажались не продумав даже базовые куйсы использования хранилища данных в их проекте. Очевидно, что социальные данные все же ближе к реляционными, чем документным, даже если просто вдуматься в смысл этих двух понятий.

Ваши же доводы сводятся лишь к одному — в MongoDB можно (и даже нужно!) эмулировать реляционность, ну и прекрасно, но зачем? Ведь при всех этих усилиях, в коде мы вряд ли сможем реализовать все те механизмы реляционности, что заложения в том же PostgreSQL. Кейс с удалениями просто не серьёзен, о чем уже написано в комментариях выше. А ведь он гораздо шире и касается в принципе подобных операций с большим набором связанных данных. С MongoDB никогда нельзя быть уверенным, что целостность не была нарушена и при этом это практически невозможно проверить. Мы в своё время придумали парочку костыльных решений, но не одно из них не может считаться удобным или правильным.

Каждому инструменту своё применение. Лично я согласен с вариантом использовать документные базы в качестве кеша или каких-то реал-тайм хранилищ, а основные хранить в целостности и сохранности. Также мне кажется что кейс с ТВ сериалами не менее реляционен, чем соц.данные. Во всяком случае, когда в нем появляются many-many связи. Из моего опыта идеальный кейс для монги — конструктор сайтов. В нем реально каждый документ монги был лишь моделью DOM в JSON формате и никаких связей особых не было. В таких проектах претензий к MongoDB нет вообще.
> Лично я согласен с вариантом использовать документные базы в качестве кеша или каких-то реал-тайм хранилищ, а основные хранить в целостности и сохранности.

Есть такая платформа — Lotus/Domino. Это крупный и очень крупный Enterprise. В основе — своё NoSql хранилище NSF. Пилят на нем в основном документооборот, ну и свой нативный почтовик, доменная книга. master-master репликация из коробки, failover, и еще черта в ступе разных наворотов. И ничего, живут без реляционных схем уже… не соврать бы, третий десяток лет.
В лотусе, насколько я знаю, задается схема данных в коллекции — атрибуты и ссылки на документы.
Нет, никакой схемы там нет.
Волею судеб, моя профессиональная деятельность началась с разработки под лотус. RDBMS в мою жизнь вошли уже позже, что ломает обычный шаблон "… а вот еще есть модный noSQL!". Общаясь, я уже не раз замечал некий ступор или какую-то «преграду», чтоли, на пути к пониманию schemeless у людей, которые изначально изучали классические принципы БД. Некоторые просто не могли принять — как это так «нет схемы»? «А как же таблицы??!»
Причем, люди эти были совсем не дураки, просто они никогда не видели альтернатив реляционному подходу и, видимо, сложилось мнение, что их нет. Для меня же после работы с lotus понять релиционный подход было довольно легко. И мне в общем совершенно безразличны вот эти холивары. Я знаю, что есть разные инструменты. Примерно представляю их + и — . И, например, в удобстве документо-ориентированных nosql хранилищ для документооборота (и подобных задач) меня не переубедит никто. =)
Вообще любая система, в которой можно проектировать формочки, имеет схему. По другому никак.

Когда говоря об отсутствии схемы, имеют ввиду что не надо поддерживать схему в СУБД и приложении. В приложении схема есть всегда.

Соответственно если средства разработки позволяют описывать схему в одном месте (генерировать таблицы в РСУБД по типам), то «преимущество» отсутствия схемы — вовсе не преимущество.
Уф, вот не люблю я эти игры с определениями =)
Под остутствием схемы я понимаю следующее:
В Lotus я могу создать application с формой, имеющей поля «Имя», «Фамилия». По этой форме создать 100 документов. Потом меняю форму, добавляю поле «Отчество». Больше я НИЧЕГО не меняя могу спокойно работать как с предыдущими 100 документами, так и создавать новые. В новых это поле будет, в старых — не будет или будет создаваться при модификации(пересохранении) старого документа. Идем дальше. Всем людям на земле имена меняют на UID. Я меняю свою форму, удаляя из нее поля Ф, И, О, добавляю одно поле — ID. И больше НИЧЕГО не меняю, при этом могу работать со всеми старыми документами (например, можно отображать значения ФИО, у кого они есть, в режиме R/O), могу создавать новые. Мне не нужно ничего менять в БД!
Или с другой стороны. Форму не меняем, пусть на ней будет Ф, И, О. Моя компания поглащает другую команию, и мы объединяем базы. Например, у них кроме ФИО еще было поле «дата рождения» (для упрощения, пусть у них тоже был Lotus/Domino). Догадываетесь что дальше? Я просто копирую документы из их базы в свою и всё работает! В их документах я вижу поля Ф, И, О в МОЕЙ форме. Поля «дата рождения» никуда не деваются, они есть в документах из импортнутой базы и я волен делать с ними что угодно — удалить, отображать опцинально в R/O, доработать свою форму для работы с этим полем.
Вот это я понимаю под отсутствием схемы. Именно так это происходит в Lotus.
Надеюсь, пример понятный и приводить контрпример для РСУБД не требуется?
В РСУБД тоже можно добавить необязательных колонок и работать с предыдущими записями без проблем. А то что касается форм — от движка СУБД не зависит вообще.

А вот гораздо интереснее что будет если в Лотусе иметь текстовое поле, а потом его заменить на ссылку. Что получится со старыми документами?
Во-первых, добавить необязательных колонок, конечно, можно. А сколько это займет на большой нагруженной БД?
Во-вторых, пример с добавлением 1 поля крайне прост, чтобы показать суть. Условно, я могу держать 100000 документов у которых вообще ни одного поля не будет одинакового. И по количеству полей они будут разные и по типам и по названиям и всё это одновременно, понимаете? И место в БД они будут занимать ровно столько, сколько занимают суммарно все эти поля. И добавление-удаление документов с любой комбинацией полей займет крайне мало ресурсов. Это вырожденный случай, но им я вам показываю, что «создать необязательные поля» это вообще не то и не об этом.

Насчет текстового поля и ссылку не понял, что за ссылки вы имеет в виду. Что-то заменить вы можете только в конкретном документе. На что замените, то и будет. А формы никак с документами не связаны, совершенно.
Во-первых нисколько ;)

Не знаю как это делает Postgres, но в MS SQL дела обстоят очень просто — новые колонки попадают в overflow страницы, в которые не пишется ничего если колонки равны null. Фактически это снижает скорость выборок в случае наличия этих полей в запросе, но пока это не стало проблемой — можно не делать ничего, а когда станет проблемой — сделать rebuild clustered index. На таблице в 200 гб у меня занимает около 3 минут на домашнем компе.

Во-вторых есть sparse columns как раз для тех случаев, когда большая часть полей будет null, добавление sparse колонки не стоит вообще ничего. Но они крайне редко применяются как раз потому что случай тысяч записей в которых колонки не повторяются — вырожденый. Даже в лотусе это нереальный сценарий, ибо нужно или добавить поле в форму, или написать код, обрабатывающий данные. Схема то на уровне приложения есть всегда. Данные без схемы с точки зрения приложения это просто blob и не более того.

Если всё прямо так, и нет подводных камней, то признаю, что MSSQL крут в этом плане. Видимо, работает на магии =)
Весьма интересно как всё-таки у постгреса в этом плане. Попробую погуглить, вполне может отстал от жизни.
Если смотреть по фичам, то MS SQL находится в конце рейтинга. Первое место делят Oracle и DB2, а MS SQL чуть обгоняет postgres (и то не по всем направлениям). Последнее место стабильно за MySQL.

MS SQL удобен только одним — разработчики активно делятся тем, как внутри устроен движок, поэтому можно много изучить про то как в принципе устроены СУБД (принципы везде одинаковые). Это позволяет практически из любой РСУБД выжать максимум и понимать когда тебя пытаются обмануть ушлые «продавцы» NoSQL.
Подумал еще.
Интересно, что некоторый набор фич MSSQL и, возможно, других взрослых СУБД позволяет сымитировать schemeless, но всё-таки это не то же самое, что, например, в Lotus. Подробно попробую расписать в другой нашей цепочке, но кратко: в случае rdbms всё равно фиксируется схема данных — с помощью набора overflow страницы, sparse columns и прочего. Т.е всё равно для таблицы (или их набора) фиксируется некое допустимое множество комбинаций или сохраняется формат записей, которые уже добавлены.
noSQL хранилище кардинально отличается тем, что каждый документ (запись) — это данные + «схема» (структура), вот и всё. Это и плюс — абсолютная свобода структуры каждого документа, и минус — оверхед на хранение структуры для _каждой_ записи(документа) и более сложные и дорогостоящие операции обработки, непохожие на rdbms.
Только плюсы и минусы — отдельная тема, я сейчас именно про отличия.
Есть ещё такой момент — возможность пользователям добавлять кастомные поля сущностям, чтобы по ним можно было фильтровать, сортировать и тп.
Насчет текстового поля и ссылку не понял, что за ссылки вы имеет в виду.

Объясняю подробнее.

Было у вас текстовое поле «клиент». Потом решили сделать отдельную коллекцию для клиентов и с некоторого времени в поле клиент хранить ссылку на документ в другой коллекции. (внимательно: это та же самая колонка).

Что будет со старыми документами?
> (внимательно: это та же самая колонка) Что будет со старыми документами?
Колонок нет, есть поля в документах. Но я все равно не понимаю. Что такое ссылка? На документ в лотусе можно ссылаться по UUID документа. Фактически, это тоже текстовое поле. Со старыми документами, безусловно, не будет ничего, если вы сами их не станете изменять =) Иначе быть и не может вроде.

Ну ок, колонка == поле. Была дата, а стала ссылка (UUID) в том же самом поле.
Давайте рассмотрим такие моменты:
Схема — она таки где-то есть. На уровне коллекции или на уровне приложения.
Схема нужна — не для того, чтобы усложнить жизнь, а для того, чтобы облегчить её. Но при этом схема несёт с собой некоторые накладные расходы. Абсолютно то же самое можно сказать про свободную схему (не отсутствующую, замечу).
И сравнивать имеет смысл итоговый результат, а не выгоды и расходы по отдельности.

А теперь чуть подробней.

Принципиальные выгоды и накладные расходы схемы:
1. Контроль корректности данных на уровне хранилища. Со схемой мы можем (не обязаны, но таки можем) требовать от клиента исполнения некоторого стандарта, без которого данные не имеют практической ценности. Например, наложить некоторые ограничения на содержимое полей «дата», «цена», «получатель» при этом ещё и в зависимости от значения поля «это черновик». Что разгружает приложение от необходимости контроля полученных из хранилища данных перед использованием. В то время как с хранилищем может работать несколько разных клиентов, отвечающих за различные бизнес-процессы и не тянущих в собе всю схему. Обратная сторона медали — модификация схемы становится узким местом.
2. Контроль над методами индексирования данных. Сплошные плюсы и соблазны. Даже не знаю, нужно ли развивать эту тему или всё достаточно очевидно? При гибкой же схеме наши индексы и запросы ещё и усложняются необходимостью фильтрации документов или возникает оверхед на обслуживание заведомо несоответствующих документов.
3. Компактность. Это существенный момент. Если мы храним миллионы-миллиарды однотипных записей, то плавно подбираемся к ситуации, когда хранить схему записи в каждой записи — начинает выглядеть глупо, особенно, если сами данные занимают меньше места, чем их схема. И это не столько вопрос занимаемого места, сколько вопрос производительности — количество дисковых чтений при доступе, а о таких живительных вещах как кеш в памяти, кеш в процессоре остаётся только горько плакать. Более того, реляционные базы идут дальше и вводят колоночные хранилища, которые ещё эффективней в этом плане, поскольку хранятся наиболее компактным образом и при обработке большого объёма данных в этом поле, обращение к диску минимально, а кеш — минимально засоряется ненужными данными.
4. Гибкость. Качество, за которое разработчики обожают NoSQL. Сегодня храним имя одним полем, завтра понадобилось разбить на три, после-завтра выяснилось, что есть люди, имя которых не вкладывается в привычное ФИО и вуаля — берём и меняем схему как нам нравится и не паримся. Однако не паримся только до поры до времени. После того, как всё это счастье отработало в продакшене и подверглось десяткам изменений, и наплодило документов самых разных версий и консистентности, логика приложения разрастается до неуёмных размеров. И вот поддерживать это legacy становится не в пример дороже. Никогда не знаешь, где можешь случайно поделить на ноль, а где вместо поля «surname» может попасться поле «fam», возникшее в результате лёгкого слияния двух баз, а колонка «дата» — это то ли дата в формате д/м/г, то ли в формате м/д/г. Конечно, предшественник, ранее писавший приложение, выполнявший слияния, испытывавший творческий порывы (свои или начальства) переделать и улучшить, всё это как-то учитывал и знал, но сейчас он в коме, а «надо уже вчера»
> Схема — она таки где-то есть. На уровне коллекции или на уровне приложения.
На уровне коллекции никакой схемы нет. Ну вообще никакой. В Lotus коллекции вообще только в runtime могут существовать, как результат выборки по БД.

Смотрите, nosql db и её свойства можно легко представить в виде коробки, в которую можно класть или доставать листы бумаги. Каждый лист бумаги = документ в терминах БД.
Так вот: вы можете взять чистый лист и написать на нем любое(!) количество любых(!) комбинаций записей в формате:
FieldName := FieldValue
И бросить этот лист в коробку-БД. И всё, БД «не знает» какие там Field'ы и их значения. Она просто хранит в коробке лист бумаги на котором записаны вот эти поля.
Я могу покапаться в коробке (с помощью функций БД, конечно), найти, например, все документы, у которых поле «Name» имеет значение «Вася», достать их, что-нибудь с ними сделать и бросить обратно.

Что такое схема на уровне приложения — я честно не понимаю. Приведите пример?

> Абсолютно то же самое можно сказать про свободную схему (не отсутствующую, замечу).
Схема, если её можно так назвать, присутствует на уровне документа. Но, согласитесь, это совсем не то же понятие схемы, что в рдбмс? Или не согласны?

> Принципиальные выгоды и накладные расходы схемы:
> 1. Контроль корректности данных на уровне хранилища.… Обратная сторона медали — модификация схемы становится узким местом.
Согласен. Корректность данных можно выполнять на уровне хранилища И/ИЛИ на уровне приложения. Хотя nosql могут позволять выполнять проверку на уровне базы, но в реляционных это безусловно выглядит естественней и проще.
Вопрос — какой вариант нужен для задачи.

> 2. Контроль над методами индексирования данных. Сплошные плюсы и соблазны.
В nosql индексы тоже довольно мощные, есть и sparse, совмещенные, с индексацией по стуктурам внутри значений полей…
Так что тут спорно.

> 3. Компактность. Это существенный момент. Если мы храним миллионы-миллиарды однотипных записей, то плавно подбираемся к ситуации, когда хранить схему записи в каждой записи — начинает выглядеть глупо, особенно, если сами данные занимают меньше места, чем их схема.
Согласен.
Я говорил об этом ранее, что хранение структуры для каждой записи — неизбежный оверхед. Поэтому называть поля «LastNameOrIdentificationNumberOfWebSiteUser» в noSql — плохая мысль =)
Только никто не спорит, что хранение реляционных данных в нерялиционной БД менее эффективно, чем в РДБМ. Очевидно.

> 4. Гибкость. Качество, за которое разработчики обожают NoSQL.
Повторюсь, да, эта гибкость далеко не бесплатна.
Но дальше у вас, в отличие от предыдущих пунктов, идет слишком много допущений. Согласитесь, что сделать плохо можно на любой системе, не важно — sql или не-sql.
Взять тот же Lotus/Domino. Он развивается десятки лет и если бы всё было так плохо, как вы пишите, я думаю у IBM хватило бы ресурсов (денег, времени, людей, технологий), чтобы пределать на ту же DB2. Однако, нет.

У меня в продакшене работают и реляционные базы и nosql в лице монги и редиса. Переход на монгу, возможно, был из-за того, что это мы не знали как эффективно решить наши задачи на постгресе, например. Возможно, крутой дорогостоящий DBA смог бы помоч, но монга помогла нам все сделать своими силами =) И ничего, шуршит уже несколько лет. В самой большой базе под 500млн документов, куча коллекций, разные структуры, да… Но что поделать, это реальности приложений, когда требования изменяются практически постоянно.

P.S. Спасибо за дискуссию!
Если честно про данную платформу ничего не знаю, поэтому утверждать не буду, но может они как раз и занимаются эмуляцией реляционности в коде? А может их NoSQL решение просто более продвинутое чем MongoDB?

Лично мне доводилось работать с MongoDB, Redis, Memcached и еще парочкой наиболее распространенных NoSQL хранилищ. И могу сказать, что не отдал бы ни одно из них под основное хранилище для большинства своих проектов. Может у меня просто проекты такие, но ведь мы с вами можем лишь делиться своим опытом и не более того. Это мой опыт, на истину не претендую, а того, кто заминусовал мой предыдущий комментарий не понимаю.
А что такое — эмуляция релиционности в коде?
В любом случае не знаю, у них закрытые форматы и код. Но как-то непонятно для чего им эмулировать то, что не нужно? Нет, подключить через ODBC можно почти любую БД, но только в качестве внешнего источника данных, но не как нативное хранилище своих объектов. Так было лет 5 назад точно. Вроде был проект скрещивания с DB2, но по-моему ничем хорошим он не закончился. Скрестить ужа и ежа, внезапно, оказалось не просто =)
Если эта штука просто конструктор форм, как я понял из ваших комментариев, то я уже писал что кейс с конструкторами (сайтов, форм, etc) являются почти идеальным вариантом для NoSQL.

Про эмуляцию довольно неплохо видно из статьи. В общем случае сводится к «если вам нужны связи между сущностями просто разнесите их разным коллекциям и залинкуйте айдишниками, которые по сути просто строка. А всю работу по поддержанию целостности, каскадности и те же самые джойны просто реализуйте ручками в коде. А главное за забудьте исправить все все ошибки, иначе если вдруг код забажит, то проверить целостность вам никак не удастся».
Sign up to leave a comment.

Articles