metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-10-24 06:39 am

NoSQL vs RDBMS: Too may FOREIGN KEYs

Сижу делаю очередную опердень, и наткнулся на печаль: таблицы содержат сильно больше внешних ключей и вообще структуры/связей (которые в FB основаны на индексах), чем собственно данных. Это после адекватной нормализации и отображения требуемой для расчетов структуры на базу данных. Типа того, что в таблице, где хранятся суммы, 4 поля, из них 3 - это разного рода ссылки на сущности "налогооблагаемый объект", "период расчета", "период уплаты", и одно поле - собственно сумма.
Причем раньше у меня было чисто технологическое ограничение - чем больше таблиц, тем больше приходилось писать вручную sql-скриптов, маппингов "таблицы-объекты", запросов для работы с таблицами. Я поэтому особо не нормализовал, шел по пути типа "нужно 12 месяцев - заводим 12 полей, нужно 4 квартала - заводим 4 поля". А сейчас, во первых, с кодогенератором это ограничение исчезло, а во вторых, 4 квартала и 12 месяцев превратились в "список периодов, диктуемый каждый отчетный год министерству по налогам и сборам Червем из астрального мира".

Т.е. если мы идем по пути "много структур данных и мало кода", который нам в голову диктует функциональщина, то отражение этого дела в RDBMS превращает ее в невероятно запутанный граф, в котором связи занимают больше места чем данные. Если бы это был NoSQL, а наши данные имели явно заметную тенденцию объединятся в объекты (т.е. работа с записями из связанных таблиц ведется всегда вместе), то мы бы просто ложили в NoSQL объект в виде xml/json/любой структурированный текст.
Бинарное представление в NoSQL было бы сильно эффективнее текста в плане размера и обработки, но, во первых для него нужна статическая типизация, схема и тому подобное (чтобы сериализация/десериализация были эффективными), и обработку этого должна делать СУБД, т.к. я совершенно не испытываю желания это все делать.
Кроме того, как уже неоднократно писалось: RDBMS работает эффективнее, т.к. ее данные, условно говоря - это последовательность бинарных записей одинаковой длины, хождение по которым - это +-длина записей. А всякая бинарщина и xml - это парсеры, сериализация и прочий плохо взаимодействующий с кэшами процессоров и выделением памяти тупизм.

В моем случае это все не применимо в принципе - 99% требуемой от разрабатываемого модуля функциональности уже реализовано в виде "Firebird+Универсальный толстый клиент на дельфи", основная обработка ведется SQL запросами и кодом внутри FB. Всунуться сюда с чем-то, хоть отдаленно похожим на NoSQL, невозможно. Т.е. постулат: NoSQL == разработка с нуля, что на данный момент неприемлемо никак.

Но вообще, даже если мы вместо чистого NoSQL придумаем какой-то хитрожопый вариант типа "статически типизированные объекты в бинарном виде", индексы по внутренностям этих объектов для оптимизации запросов, функциональщину в качестве языка запросов, то все равно останется проблема с тем, что объекты теперь не представимы в виде записей постоянной длины (хотя сейчас больше проблема с дисковым i/o, чем с нагрузкой на проц) и что все равно как-то придется идентифицировать объекты независимо от их физического расположения - а это означает опять ID, опять индексы, сопоставляющие ID и физическое расположение, опять связи по этим ID и прочая и прочая.

[identity profile] dair-spb.livejournal.com 2010-10-24 08:11 am (UTC)(link)

12 месяцев - 12 полей - это как???

[identity profile] metaclass.livejournal.com 2010-10-24 08:22 am (UTC)(link)
Ну классическая денормализация массивов с известной заранее длиной:
create table sometable(
ID
...
M1 numeric(18,4),
M2 numeric(18,4),
...
M12 numeric(18,4)
);

Хорошо в том случае, если не нужно делать запросы вида "найти месяцы в которых сумма была меньше заданной" и когда суммы гарантированно есть каждый месяц.
Иначе лучше сделать отдельную таблицу (ID,Month,Total)

[identity profile] kkirsanov.livejournal.com 2010-10-24 08:31 am (UTC)(link)
--Иначе лучше сделать отдельную таблицу (ID,Month,Total)
А зачем тут ID?

Разве Month не однозначно идентифицирует что за месяц имеется в виду, или возможны разные Total с одинаковым Month?

[identity profile] metaclass.livejournal.com 2010-10-24 08:50 am (UTC)(link)
Месяц - 1-12, ID - ссылка на годовой документ

[identity profile] metaclass.livejournal.com 2010-10-24 08:55 am (UTC)(link)
Причем "годовой" документ может быть не ровно за год, иначе можно было бы обойтись номером года вместо ID.

[identity profile] volodymir-k.livejournal.com 2010-10-24 11:08 am (UTC)(link)
Какой смысл у этой заметки? Сказать, что в этом случае носкл непригодны? Ну да. Они пригодны, если данные имеют сильно выделенную крупную центральную сущность типа "клиент" и не нужно часто делать запросы вида "дайте сумму по поступлениям от бобруйских пенсионеров за чётные недели года через яндекс-деньги." Как только идёт перекос в сторону равноправия сущностей (отчётные периоды, услуги и клиенты) и нужны сложные запросы -- сливай воду.

[identity profile] metaclass.livejournal.com 2010-10-24 11:17 am (UTC)(link)
Не, что NoSQL тут не пригодны, это отдельная история. Меня больше напрягает момент, что RDBMS для сильно сложных графов (например "состояние машины редукции графов") тоже практически непригодны. А меня интересует идея, можно ли автоматически маппить функциональные языки на базу данных и обратно.

[identity profile] norguhtar.livejournal.com 2010-10-24 11:27 am (UTC)(link)
>А меня интересует идея, можно ли автоматически маппить функциональные языки на базу данных и обратно.
Объектные можно. Вот как маппить функциональные не понятно.

[identity profile] avnik.livejournal.com 2010-10-24 01:48 pm (UTC)(link)
Или пихать в nosql сераилизовыные объекты строить индексы внутре (см слово на букву z)

[identity profile] norguhtar.livejournal.com 2010-10-24 11:25 am (UTC)(link)
Too may FOREIGN KEYs => use ORM!
Офигенно удобно, когда по большей части они скачут по управляющей структуре. Один черт количество SQL запросов будет сравнимо что при чистом SQL что при ORM, а мозгоклюйства на порядок меньше.

[identity profile] metaclass.livejournal.com 2010-10-24 11:35 am (UTC)(link)
Вот у меня кодогенератор ORM и делает, собственно.

[identity profile] norguhtar.livejournal.com 2010-10-24 12:01 pm (UTC)(link)
Ну а чего тогда за плач ярославны? :)

[identity profile] metaclass.livejournal.com 2010-10-24 12:11 pm (UTC)(link)
Ну из одной таблички за день хранилище для модуля опердени разрастается до 7 таблиц, переплетенных внешними ключами и это никого больше не парит, т.к. руками это писать не нужно :)

[identity profile] sergiej.livejournal.com 2010-10-24 01:26 pm (UTC)(link)
Ну и пусть разрастает если производительность не особо волнует.

[identity profile] norguhtar.livejournal.com 2010-10-24 02:44 pm (UTC)(link)
Эт нормально. Главное чтобы нормализовано было и записано было нахрена это все надо.

[identity profile] sergiej.livejournal.com 2010-10-24 01:33 pm (UTC)(link)
IMHO Денормализация нужна только когда проблемы с производительлностью. Сложность запросов это не беда, если особо не лениться то хоть семиэтажные маты селекты и пишутся и читаются человеком, ну а при ORM ваще нет о чём волноваться.
А вот ежели одна "логически" табличка (читаемая с одной формы или поиска) рассыпалась по 14-ти разным, и при этом 2 тысячи юзверей одновременно этот запрос юзают - прямая путя в денормализацию, запихнул всё в плоскую таблицу (ну или материализовывать) и будет полегче, и плевать на кошерность базы, ибо апгрейд железа для "нормализованной" базы начинает кусаться.

[identity profile] tretiy3.livejournal.com 2010-10-24 08:18 pm (UTC)(link)
не врубаюсь в чем разница между первым и вторым утверждением:
cемиэтажные селекты и пишутся и читаются человеком
и
вот ежели одна "логически" табличка (читаемая с одной формы или поиска) рассыпалась по 14-ти разным
считаете что 7 джойнов - допустимо, а 14 - не допустимо?

[identity profile] sergiej.livejournal.com 2010-10-25 08:19 am (UTC)(link)
Читайте предложение до конца: "и при этом 2 тысячи юзверей одновременно этот запрос юзают "
При большой нагрузке - допустимо, но нежелательно.

[identity profile] gds.livejournal.com 2010-10-24 07:52 pm (UTC)(link)
не совсем в тему.
Как-то думал над гибридом sql+nosql: по умолчанию все однотипные сущности хранятся в таблице (id:number * serialized:blob). Отдельно хранится "схема" -- что в serialized соответствует чему (типы, т.е.). Запросы писать на sql (да пофиг, на чём), но всё, что касается условий над аттрибутами/связями, преобразовывать в get_mytype_attr(blob, 'my_attr'). То есть, выполняться оно будет, как факт. Далее, как ключевой момент идеи, научиться переносить аттрибуты из сериализованного представления в отдельные столбцы. Переносить -- либо вручную, когда очевидно, что и как будет использоваться, либо по результатам профайлинга. Перенос аттрибута можно оформить полупрозрачным образом: посмотреть, умеет ли субд экономно хранить ещё один блоб, содержащий почти всегда нуллы, и на каждую "миграцию аттрибута" в фоновом режиме заполнять второй блоб (с учётом того, что могут проапдейтить то, что уже перенесено: заставить все update обнулять значение второго блоба). Или же блокировать любой доступ к таблице, добавлять столбец, модифицировать блоб in-place.
Хотя, прикидываю, в большинстве случаев такое нафиг не нужно. С другой стороны, если бы я использовал nosql в больших количествах, то, зная преимущества реляционок, был бы не прочь иметь такое решение.