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

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

Я не специалист (хотя я работал и с мускулем и с пакетом Elastic (кибана, битс, логстэш) + апачевский кафка), и мне кажется, что сравнивать SQL и NoSQL базы без привязки к области применения, не совсем корректно. Где-то «тащат» нереляционные БД, а где-то — реляционные.

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

Лично передо мной сейчас дилемма: есть приложение использующее реляционную базу и она отлично подходит для текущего функционала, но в планах добавить новые фичи, которые без костылей не реализовать в *SQL, а в монго они «взлетят с пол пинка».

Кроме легкого шардинга, что?


Естественно, если сравнивать с Postgres, а не с MySQL.


P.S.: Я вообще предпочитаю связку Postgres + Redis для OLTP+, хотя последний не нравится многим (ну так путают области применения).

Сейчас я тоже использую постгрес, но новые фичи предполагают использование разного количества полей у хранимых объектов, то есть придется хранить json в уже существующих таблицах и городить дополнительную логику в приложении для его разбора поверх ORM. Плюс мне очень желателен быстрый UPSERT. Плюс простое версионирование некоторых свойств объектов. Пока я в процессе поиска решения в рамках постгреса, потому что не хочется терять полноценные транзакции для финансовой части приложения. Но пока результата нет, вот и думаю собрать тестовую версию с монгой и проверить скорость основных операций — возможно в итоге буду использовать 2 базы под разные части.

Мы используем JSONB-поля, всё хорошо. Индексы на них строятся, что радует безмерно.


Рекомендую не городить логику в приложении, а форкнуть ваш ORM и влить функционал JSON туда. И отдать в upstream обратно.


Польза всем, и приложение будет проще.


P.S.: да, мы тоже просто расширили ORM.
P.P.S.: Postgres для биллинга и что-то иное для контента — имеет право на жизнь, все зависит от потребностей.

Поддержка schemaless в библиотеках/фреймворках, ориентированных на Mongo. Постгресс базу тоже можно свести к schemaless, оставив в каждой таблице два поля: id и jsonb data (попутно выкинув кучу таблиц часто), но вот поддержка таких SQL типов в различных ORM оставляет желать лучшего.

Допустим, но как будем делать update одного поля в большом json документе ?

Обновлением документа в целом. А там база разбирается как она это будет делать.

Ну вы шутите чтоли? При значительном объеме документа, ваш сервер сразу же захлебнется по CPU на сериализации-десериализации.

Справедливости ради, даже при 1000-2000 входящих ежедневно (а это достаточно крупная компания) для современного сервера это не нагрузка
НЛО прилетело и опубликовало эту надпись здесь
Я как-то было дело одновременно наблюдал как мои коллеги из одной команды переписывали логику с SQL на Java (потому, что нужна была правильная архитектура, модульность, тесты, всякое такое), а во второй команде переписывали тоже логику с Java на SQL (потому что архитектура — это хорошо, но SQL работает быстрее)
:)
НЛО прилетело и опубликовало эту надпись здесь
У меня опыта с MongoDB нет, но насколько я понял из статьи — его имеет смысл использовать для простых CRUD-приложений с перспективой быстрого роста, когда при этом не очень страшно, что часть данных будет неконсистентной. То есть действительно информация по онлайн-играм, сообщениям и прочей пользовательской активности.

С другой стороны, если у меня точные данные или нужна сложная аналитика, то тут уже придется брать *SQL базу на одной машине с репликами, и надеятся, что до шардинга дело дойдет нескоро.
его имеет смысл использовать для простых CRUD-приложений

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

В 2017 году вы уже не пишете на SQL, вы используете ORM для 90% операций. И логика оказывается в коде, где ей и место.
Видимо, минуснули люди, гордые тем, что умеют написать SQL запрос. Ну молодцы, да.
А я пишу на SQL больше 10 лет, преподаю его, и в том числе мне пришлось писать всякий ад на PL/SQL. И после этого использовать ORM это счастье. Логика остаётся в коде, снижается порог входа для разработчиков, и в любой момент можно перейти на любую другую РСУБД щелчком пальцев.
В смысле «снижается порог входа для разработчиков»? Тут уже кто-то писал, что, если SQL сам по себе — это барьер, то разработкой, наверное, не стоит заниматься вообще.
Скорее всего, вы не в курсе, но существует много диалектов SQL. И обычно разработчик знает один из них. И заставлять его думать над тем, как в очередной СУБД реализованы простейшие вещи вроде лимитов выборки, строковых функций, автоинкрементных полей и так далее — просто глупая потеря времени. Хватает прописать всё в ORM, а потом проследить, что была создана адекватная струтура и сгенерированы оптимальные запросы. SQL при этом в любом случае must have — но для проверки, не для написания кода.

Но гордые знатоки однострочных запросов MySQL минуснут и этот комментарий.
Вы так активно пропагандируете ORM, что мне даже стало интересно. Может, напишете статейку? Видите, народ (и я в том числе), не знает все мега-плюсов этой панацеи. Не поленитесь, просветите народ, вам спасибо скажут.
Статьи писать люблю по темам, на которые до меня не писали. А про ORM всё было сказано уже тысячу раз. Как пример, можете почитать про query builder в yii2 в php или про knex в node.js. На том же хабре про всё это писали много раз.

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


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

ORM снижает требования к качеству среднестатистического разработчика — это его основной плюс и выгода для бизнеса.
Это как бы не сложнее чем знать все диалекты.

Тут на самом деле много вариантов.

Самый популярный — забить и считать, что ORM всё знает лучше.
Спойлер
На самом деле нет!

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

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

ORM снижает требования к качеству среднестатистического разработчика — это его основной плюс и выгода для бизнеса.

Если бы технология была выгодна только для бизнеса, бизнес бы о ней никогда не узнал (кто б ему рассказал), да и до масс это бы не дошло. Всё же, хоть доля истина в этом есть, правильный ответ немного другой. Согласен в данном случае со stack overflow — это помогает писать достаточно абстрактный и портируемый код. К примеру, сейчас у меня есть pet project, который мы пишем с несколькими разработчиками. При этом у меня mariadb, а у них postgres. И почему бы и нет.

Если бы технология была выгодна только для бизнеса, бизнес бы о ней никогда не узнал (кто б ему рассказал)


какая наивность…
1. ORM и Highload несовместимы
2. Так как существует как ты говоришь десятки диалектов SQL, существует десятки разных ORM.
3. Бывают заковыристые запросы, которые никак не написать на ORM, и нужно опять писать на SQL.

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

С точки зрения MongoDB, здесь преимущество то, что у нас гибкий JSON-формат документов. Для некоторых задач и каким-то разработчикам это удобнее, чем мучиться с добавлением колонок в SQL-базах данных. Не нужно учить SQL — для некоторых это сложно. Простые запросы реже создают проблемы. Если посмотреть на проблемы производительности, в основном они возникают, когда люди пишут сложные запросы с JOIN в кучу таблиц и GROUP BY. Если такой функциональности в системе нет, то создать сложный запрос получается сложнее.

Это уже через чур. Ощущение, что тебя разводят.

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

Часто встречаюсь, что SQL и ко настолько проникли в мозги разработчиков, что таблицы и связи городят даже там, где их быть не должно в принципе, не говоря о том, где можно решить задачу другими способами не менее эффективно. Вот на днях проблемка возникла у бизнеса — есть некий документ, который оформляет пользователь системы, на печатной форме документа его ФИО стоит. Разработчики, создавшие систему изначально, не мудрствуя лукаво сделали в таблице документа поле user_id, ведь ФИО есть в таблице пользователей. Нормализация наше всё и т. п. Но не учли, что людям, особенно девушкам, свойственно иногда менять ФИО, а на печатных формах ФИО пользователя должно быть на момент, грубо говоря, создания документа.


отсутствие схемы очень редко, когда действительно нужно

Полное отсутствие, да. Но очень часто нужно либо часто менять схему, либо часто добавлять новую, но очень близкую к имеющейся. Часто это вырождается либо в одну таблицу с кучей (реально куча, даж не десятки) nullable полей, либо в сложные схемы типа одна главная талица, десяток связанных 1:0..1 и имя таблицы с которой связь в поле главной, что приводит к чему-то типа:


SELECT * FROM main_table
LEFT JOIN table_1 ON main_table.id = table_1.id AND main_table.type = 'table_1'
-- ...
LEFT JOIN table_10 ON main_table.id = table_10.id AND main_table.type = 'table_10';
Это больше похоже на недоработку в проектировании БД. А не в выборе типа БД.

А почему просто не мигрировать на новую схему?

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

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


К реляционным базам это имеет никакое отношение.

В вашем конкретном случае документы должны были иметь два поля user_id, user_fullname, например.

Это если нигде более смена фамилии не является критичной.
В противном случае вам придётся вынести список фамилий и имён пользователя выносить в отдельную сущность и хранить в отдельной таблице user_id, firstname, lastname, date_create…

Но интересен другой вопрос.
MongoDB и прочие schema-less решения каким образом внезапно спасли бы вас в описанном вами случае?

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


Внезапно, да. По умолчанию в них бы внесли значение user_fullname, если необходимость какой-то нормализации и дедупликации явно не задана в ТЗ, просто чтобы не париться. "Реляционщики" чтобы не париться нормализуют по умолчанию, где надо и где не надо, чтобы обеспечить дедупликацию ценой использования JOIN-ов, а "документщики" чтобы не париться денормализуют где надо и где не надо, чтобы избежать запросов подобных JOIN-ам.

Внезапно, да. По умолчанию в них бы внесли значение user_fullname

Ну таки в ловушку попались.

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

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

То есть в документе зафиксирован Вася Пупкин, а вот самих Пупкиных в вашей БД при этом может не быть вовсе теперь.

Так что, как вы справедливо указали, проблема именно в

не вникая ни то, что в бизнес-процессы


И schema-less в данном случае просто создают другой тип ошибки неконсистентности данных
Естественно имелось в виду наряду с user_id.

В чём разница межды мышлением «реляционщиков» и «документальщиков». Первые по умолчанию стараются оставить только id, вынеся всё остальное в отдельные таблицы, рассчитывая на джойны, а вторые стараются кроме id внести всё, что может понадобиться чтобы избежать джойнов.

Но вообще, для документарных баз вполне нормальна ситуация, когда наличие ссылки в одних документах на другой не препятствует удалению последнего. Проще говоря, даже если удалим пользователя из коллекции, то не будет ошибкой пустой результат при поиске в коллекции пользователей, ведь все нужные для заказа данные пользователя у нас сохранены непосредственно в документе заказа. Собственно user_id там на всякий случай обычно. Реляционные же «заставляют» нас делать либо каскадное удаление, либо вводить какой-то атрибут «уволен» и учитывать его в одних процессах, типа на кого назначить заказ и не учитывать в других типа печати документов.

А обнулить user_id реляционные БД не дают возможности?

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


Вообще есть две красные тряпки для "реляционщиков":


  • нарушение ссылочной целостности
  • дупликация данных

Они, грубо говоря, приходят в двойное бешенство если видят в таблице order 1000 записей user_id=1, user_name = "Иванов Иван Иванович" и при этом в таблице user нет записи c id =1 и вообще в таблице order нет ссылочных ограничений на поле user_id. Разработчика такой схемы они автоматически объявляют бэдлокодером, раз он дублирует данные и не поставил ограничение. Даже не поинтересуются были ли в постановке задачи соответствующие требования.

Вы уверены, что эти красные тряпки существуют не только у "реляционщиков" у Вас в голове? Как и сами "реляционщики"?

Я уверен, что они существуют у "реляционщиков", как и в том, что они сами существуют. Прежде всего это те, кто нереляционные базы не использовал ("нет джойнов и ссылочной целостности? даже время на посмотреть тратить нет смысла"), а при знакомстве с проектом в первую очередь лезут в базу данных, а потом долго возмущаются, что референсы там не прописаны, и вообще таблицы не маппятся на объектную модель 1:1, а то есть таблицы которые в объектной модели не представлены, а то есть объекты которые в базе хранятся, но таблицы для них нет.

Реляционные же «заставляют» нас делать либо каскадное удаление, либо вводить какой-то атрибут «уволен»


Считаете неправильным хранить значимую историю изменений?

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


Правильное моделирование сущностей типа заказа заключается в копировании всех значимых для заказа данных из текущего состояния сущностей типа клиент и товар, собственно даже идентификатор этих сущностей в заказе особо не нужен, кроме как для аналитических целей типа посчитать сколько заказов сделал конкретный клиент или сколько конкретного товара заказано. Но "лучшие практики" реляционного моделирования "заставляют" нас из заказа только на сущности ссылаться, дублируя только те данные из сущностей, которые явно в ТЗ указаны как подлежащие дублированию. Либо вводить дополнительные сущности типа "состояние сущности такой-то на такой-то момент" и ссылаться на них явно через идентификатор, либо через составной ключ (часто неявный, без ограничений reference) типа ссылки на основную сущность и дату на которую нужно фиксировать изменение.

Эм, у вас какой-то свой взгляд на реляционную модель.
Базой ее является то, что данные представляются в формате связей.

Вас кто-то знатно потроллил на тему нормальных моделей.

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

Правильное моделирование сущностей типа заказа заключается

Неа, не заключается.

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

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


Именно, что тупо копирую. Моя задача как разработчика автоматизировать имеющиеся бизнес-процессы, зачастую заданные даже не бизнесом, а государством.

Именно, что тупо копирую. Моя задача как разработчика автоматизировать имеющиеся бизнес-процессы, зачастую заданные даже не бизнесом, а государством.

Фиговые у вас аналитики, раз ставят именно такие задачи.

Например, тот же НБУ еще в 1999 году издавал соответствующие постановления об электронном документообороте. И там была формализация результатов, а не процесса.

2.1.1. Вимоги до первинних облікових документів
Підставою для бухгалтерського обліку операцій банку є
первинні документи, які фіксують факти здійснення цих операцій.
Первинні документи повинні бути складені під час здійснення
операції, а якщо це неможливо — безпосередньо після її закінчення
та можуть складатися у паперовій формі та/або у вигляді
електронних записів (у формі, яка доступна для читання та виключає
можливість внесення будь-яких змін). У разі складання їх у вигляді
електронних записів при потребі повинно бути забезпечене отримання
інформації на паперовому носії.
Первинні документи як у паперовій формі, так і у вигляді
електронних записів (непаперовій формі) повинні мати такі
обов'язкові реквізити:
— назву документа (форми);
— дату складання документа; { Абзац п'ятий підпункту 2.1.1
пункту 2.1 із змінами, внесеними згідно з Постановою Національного
банку N 283 ( z0675-01 ) від 18.07.2001 }
— назву підприємства (банку), від імені якого складений
документ;
— місце складання документа;
— назву отримувача коштів;
— зміст операції (підстави для її здійснення) та одиницю її
виміру; { Абзац дев'ятий підпункту 2.1.1 пункту 2.1 із змінами,
внесеними згідно з Постановою Національного банку N 203
( z0926-12 ) від 23.05.2012 }


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


А это скорее от тяги попробовать все новое. И раз есть — надо применять…

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

Скорее именно "раз есть — надо применять". Причём часто инструментарий разработки лепит ограничения, если его специально не проиформировать, что не надо.

Кмк, здесь проблема сложнее, чем кажется на первый взгляд.


При нормализации избыточные данные обычно заменяются на связи между сущностями. Под сущностью, как правило, подразумевается первичный ключ плюс текущее состояние. Здесь имеет значение не текущее состояние, а состояние на определенный момент времени. Это не денормализация, это связь с конкретным состоянием сущности.


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


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


В общем случае надо делать ссылки на состояние объекта. То есть объект это просто primary key + state id. А state хранится отдельно. Там, где всегда требуется текущее состояние, используется object_id, там где конкретное object_state_id.


Это конечно теоретические рассуждения.

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

А, ну вот, как раз все сходится. Сущность это primary_key + state_id, значит state_id это объект-значение. Объекты-значения составляют множество, элементам одного множества можно сопоставить элементы другого множества. Например целые числа. То есть идентификатор состояния это не первичный ключ, а просто короткое обозначение значения.

state_id является значением — ссылкой на сущность state. Я же говорю, что объекты-значения плохо ложатся на реляционную модель, по крайней мере на классический SQL. Для их эмуляции нам приходится вводить новые сущности. Причём часто забывают о нормализации — для двух Ивановых Иванов Ивановичей скорее всего будет две записи значений, а не одна. Таблица с ФИО скорее всего будет таблицей истории ФИО конкретного лица, а не множеством всех ФИО без повторений.
А что мешает создать таблицу history для фио?
Как минимум сложность выборки из неё. Не каждый оптимизатор хорошо выполнит запрос типа
SELECT
  order.*,
  (
    SELECT full_name
    FROM history
    WHERE history.user_id = order.user_id AND history.date <= order.date
    ORDER BY history.date DESC
    LIMIT 1 
  ) 
FROM order 

или ещё какую его вариацию, пускай
SELECT
  order.*,
  history.full_name
FROM order
  INNER JOIN history ON history.user_id = order.user_id AND order.date BETWEEN history.valid_from AND CASE WHEN history.valid_to IS NOT NULL THEN history.valid_to ELSE current_timestamp END

Не говоря о том, насколько если усложнит логику приложения, если задачи отслеживания истории как таковой не стоит.
Все это решаемо, и не обязательно сложно. В основной таблице продолжаем хранить актуальную фамилию, к history обращаемся только при необходимости.
Никто же не говорит, что не решаемо. Но такие модели в принципе не очень хорошо ложатся на реляционную модель. Она хорошо решает задачи как раз где нужна только актуальная информация.
Любая модель не гарантирует полного соответствия реальности.
Реальность нам не нужна обычно. Нам нужна модель, используемая в бизнес-процессах. История значений каких-то атрибутов частый элемент таких моделей, но на реляционную модель она ложится плохо, в частности потому что реляционная модель оперирует неупорядоченными множествами, а история обычно нужна упорядоченная. Приходится в реляционную модель вводить дополнительные атрибуты типа «период актуальности», чтобы получить нужный результат. Или получать его вовне реляционной модели, используя сортировки и лимиты, оконные функции и т. п. от реляционной получая объединение SELECT * FROM person p LEFT JOIN person_name_history pnh ON p.id = pnh.person_id, и обрабатывая его либо на стороне SQL (ORDER BY, last_value() OVER и т. п. — это не реляционные операции), либо вообще приложения
Да, нужно сделать какие-то движения. А в nosql решении разве все произойдёт автоматически?

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

Сами списки для некоторых вещей дороже. И менее гибкие.

И большой проблемой становится когда нам надо иметь более 1-2 порядков элементов в списках, да еще с фильтрацией

Реляционная модель ещё меньше заточена под упорядоченные списки.

Вот на днях проблемка возникла у бизнеса — есть некий документ, который оформляет пользователь системы, на печатной форме документа его ФИО стоит. Разработчики, создавшие систему изначально, не мудрствуя лукаво сделали в таблице документа поле user_id, ведь ФИО есть в таблице пользователей. Нормализация наше всё и т. п. Но не учли, что людям, особенно девушкам, свойственно иногда менять ФИО, а на печатных формах ФИО пользователя должно быть на момент, грубо говоря, создания документа.

Тут проблема не в SQL, а в ДНК того, кто проектировал workflow.
Если у объекта со временем может меняться какой-то атрибут (ФИО юзера в данном случае, а еще могут меняться паспортные данные, размер достоинств и т.д.), то надо завести отдельную таблицу и там хранить историю этих изменений. В случае очаговости таких явлений (не все юзеры женщины, не все женщины выходят замуж «после того как», не все вышедшие замуж меняют фамилию и т.д.) это будет намного выгоднее с точки зрения использования ресурсов, чем во всех документах жестко забивать в шапку этот реквизит. Особая прелесть вылезет когда выяснится, что атрибут объекта изменен «задним числом» или при изменении атрибута допущена ошибка. В случае таблицы с историей изменений редактируется только запись в ней, и не нужно бегать как умалишенному по всей БД в поисках этих ФИО, проверке их на необходимость исправления и собственно исправлении — обязательно что-то будет упущено.

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

Ага, стандарт SQL-92 не в полном объёме. Очень даже захочется!

"достаточно простой" — это правда, если не вешать на сторону СУБД кучу логики в хранимках и т. п. Простая такая миграция прежде всего в смысле того, что не придётся кардинально менять принципы хранения данных.

Я к тому, что категорически не понимаю, зачем выбирать MySQL изначально. Чтобы программист БД страдал? Или по принципу «мы ничего сложнее левого джоина делать не будем»?

Как минимум, чтобы не страдали админы. :) По крайней мере очень широко распространено мнение, что мускуль проще в администрировании, чем постгри, админам с ним меньше возиться, не нужно перезагружать на каждый чих и вообще, с чем я субъективно после 15+ лет работы с мускулем и года с постгри согласен — писать почти без разницы под что, у каждой из систем есть свои нюансы, а вот админить постгри сложнее. А программиста БД чаще всего на проекте и нет, его роль выполняют в лучшем случае обычные программисты, а то и вообще ORM.

Тоже субъективно, но разницы не заметил. Более того, у меня есть сервер, на котором всё моё админство постгреса ограничилось командой apt-get install postgresql, и который при этом достаточно резво обрабатывает базу в пару десяток гигов и прожёвывает запросы с несколькими джоинами и подзапросами по таблицам содержащим несколько миллиардов записей. За последние 4 года ребутил я его только для того, чтобы обновить.
А пользователь только postgres и работает только локально?

Для использования с ORM возможностей достаточно.

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

От количества записей в таблице ORM не зависит.

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

На моей практике ORM буксуют обычно в двух случаях:


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

я для себя ответ на вопрос из заголовка сформулировал так:


  • если отношения между объектами представляют собой дерево, то документно-ориентированные БД — отличный выбор. Например, мы пишем "Кинопоиск", где нам нужно хранить всякую разную информацию о фильмах:

{ "title": "The Matrix", "year": 1999, "cast": [ {"name": "Keanu Reeves", ... } ] }


или сериалах:


{ "title" : "Big Bang Theory", "series" : [ { "number" : 1, "actors" : [ { "name" : "Kelly Cuoco" } ] } ] }


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


  • но если в графе отношений между сущностями есть циклы, т.е. один объект может быть одновременно связан с несколькими, реляционные БД — более мудрый выбор.
    Например, мы пишем Хабрахабр, где у нас есть посты и комментарии. У постов есть авторы, у комментариев так же есть авторы, то есть авторы относятся одновременно и к комментариям, и к постам.
    Если мы используем MongoDB, придётся делать или так:

Пост:
{ "title": "Заголовок Поста", "author": { "name": "username", } }


или пойти на ухищрения со ссылками, о которых упоминал в своём докладе Пётр:
{ "title": "Заголовок Поста", "author_id": ObjectId("58b1422426353d52d75f5cc9") }


Оба варианта заставят нас реализовывать логику с удалением, сохранением консистетности ключей на уровне приложения. Зачем это делать, когда реляционные БД умеют это делать за нас, и такая структура данных для них более естественна — непонятно. Мне кажется, что в этом случае выбирая документноориентированную БД, мы гораздо больше проигрываем, чем выигрываем

Вы сделали абсолютно неверный вывод. То, как вы описали, — так не выбирают БД. Структура данных — это лишь часть вопроса (в приведённом вами примере можно всё отлично организовать и в мускуле). Надо смотреть количество запросов к базе, как часто база апдейтится, как и кем пополняется, насколько критичен поиск… — вот эти аргументы, но никак не древовидность структуры данных.
Что простите? Если важна консистентность данных, то тут только реляционные БД. sad0vnikov верно написал. То, о чём вы говорите — это уже вторично. Не надо выдвигать производительность на передний план, когда важна сама информация и связи.
&gt вместо простого знака «>». Не очень читаемо, на мой взгляд.

Странно, что вы пишете амперсанд — и в коде и в тексте. В монго операторы со знака доллара начинаются.

А так, логика формата операторов вполне понятна. Чтобы не спутать оператор с названием поля, он начинается со специального знака. Символ >, кстати, вполне может быть названием поля (так что его нельзя использовать в качестве оператора) — в названии поля можно использовать любые символы из UTF-8 (кроме точки в любой части строки или знака доллара в начале строки). И это вполне читаемо, потому что все операторы созданы по одному и тому же принципу: знак доллара + текст, по которому часто (не всегда, но часто) и без документации понятно, что происходит.
Там опечатка в статье. Для такого условия используется оператор $gt.
Интересная опечатка получается. Дело в том, что если написать &gt в тексте, то он превращается в символ > средствами HTML. И чтобы реально написать &gt, нужно вводить такой код: &amp;gt. Этот код на автопилоте обычно не вводится, потому что он требует дополнительных осмысленных телодвижений. То есть, в этот момент нужно реально верить, что команды в mongo начинаются с амперсанда, чтобы осознанно это вставлять в текст. :)

MongoDB позволяет попробовать иерархическую БД, ведь активно их использовали в 60-х.

И там же их и оставили, потому что это ад.

LDAP'ы всякие (Apache Directory, MS AD, OpenLDAP, 389ds, OpenDS) живут и здравствуют. Но местами, конечно, ужасны xD

Ага, аббревиатура от Active Directory это совсем не совпадение.

Стоит также помнить, что LDAP — это Lightweight Directory Access Protocol, до этого был ещё X.500, спеки которого надо заставлять читать врагов.

если для человека изучить SQL слишком сложно, то, пожалуй, ему не стоит заниматься разработкой вообще

Некорректно противопоставлять SQL и CRUD: CRUD — это парадигма, а не конкретный красивенький синтаксис

Предпочту MongoDB Postgres-у any day с административной и интеграционной точки зрения.
Postgres шагает в нужную сторону, но пока там не будет автоматического надёжного failover и поддержки streaming replication хотя бы с меньшей версии к большей, я так и буду плакать, когда нужно обновить версию или перезагрузить подлежащую железку. Существующие же решения для организации кластера требуют тонкой настройки, иногда разваливаются и не совсем прозрачны для приложения.
Разумеется это всё про [near] zero-downtime, если этого не требуется, то как БД Postgres лучше почти со всех сторон.


А, ну и .NET-драйвер для Postgres просто ужасен (в плане производительности и дизайна обработки ошибок), правда.

Заодно напишу про денормализацию в рамках MongoDB. Большинство примеров и туториалов фокусируются на неизменяемых данных относительно небольшого размера. В реальности, если поместить даже несколько сотен комментариев к посту в один документ, "денормализовав" туда никнеймы пользователей и прочие элементы, то вас ждёт жестокое разочарование, потому что под нагрузкой это будет безбожно тормозить при материализации изменений, потому что MongoDB оперирует документами, а не полями (особенно грустно с массивами).
Основой же бонус, который я вижу, это отказ от join и логики на стороне БД. Есть исключения, конечно, но разделение read models и write model обычно полезнее, чем попытка сделать вид, что одни и те же данные используются всеми клиентами одинаково. В некоторых БД есть materialized views, но и это может быть не самым оптимальным решением при нетривиальной фильтрации.

Ещё легко упереться в ограничение в 16M, что тоже больно. Причём в некоторых драйверах в это ограничение вполне можно упереться имея не столь большие документы, но большой bulk update.

Отличная статья, огромное спасибо автору! Для начинающего server-side разработчика самое то!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации