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

На пути к функциональной СУБД и NoSQL ERP: хранение остатков и расчет себестоимости

Время на прочтение10 мин
Количество просмотров4.6K
Всего голосов 14: ↑11 и ↓3+8
Комментарии88

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

Это все конечно классно, но вопрос, как вы собрались производительность без индексов обеспечивать? То есть нужно оборотную ведомость по одной группе товаров с даты по дату сформировать. SQL может построить план с пробегом по индексам, в том числе составным. А как вы это в NoSQL делать собрались?

Ну и с ACID я так и не понял, что вы собираетесь делать. У SQL есть вполне декларативная и простая модель с блокировками (более) или update conflict'ами при RR/SERIALIZABLE уровнях изоляции. Как вы тот же остаток предлагаете целостным поддерживать?
1) Индексы нужны для прямого доступа к данным любой глубины, отсюда и сбор статистики, и партиционирование. У нас нет такой необходимости — объем иммутабельных данных конечен, и цена перебора данных непосредственно в памяти соизмерима с поиском по нескольким индексам и поднятию блока из файла (как делают СУБД). Проблема, если мы захотим нестандартную свертку за старые периоды (например с 12 по 28 января прошлого года) — тогда это фуллскан. Хотя на самом деле нет, ведь в этом конкретном случае мы знаем диапазон timestamp, а значит можем прикинуть смещение в файле JSON, а далее seek() + read() за конечное время. Но это уже тема индексирования последовательного хранилища.

2) Остатки не нужно блокировать, остаток при каждой операции генерируется новый. Блокировать нужно кэш на момент расчета этого остатка, самое простое решение — мьютекс на коллекцию, реально конечно придется заморочиться с уровнями изоляции, но сейчас у меня задача только прототипирование идеи, принципиальных проблем не вижу, если все данные в памяти, то наложить блокировку уж точно не сложнее, чем в РСУБД.
1) Не совсем понял про цену? Каким образом цена перебора всех данных соизмерима с поиском по нескольким индексам? У них алгоритмическая сложность на порядок отличается (один — n, второе log(n)). Вы наверное забываете, что сейчас процессор узкое место (так как их производительность практически не растет), с памятью (со случайным доступом, как оперативной так и постоянной (SSD)) сейчас проблем нет, и можно хоть всю SQL СУБД в память положить. То есть закладываться на медленные последовательные HDD (и большой оверхед на долгое чтение блоков) в современном мире никакого смысла нет.

Ну и не понял зачет этот огород со смещениями и seek и read, если SQL умеет это делать сам и из коробки.

2) Ну то есть ручные пессимистичные блокировки. Опять-таки SQL это из коробки, а главное куда эффективнее за счет оптимистичных блокировок умеет делать.

То есть я так и не понял чем вас SQL не устраивает?
Это мы уже в детали реализации погружаемся, я не спорю, придется решать все те же проблемы что решены в СУБД. А чем не устраивает SQL я уже ранее писал — мы не говорим о СУБД вообще, мы говорим о конкретном применении — учетная система. Ни одна из известных мне систем не масштабируется до уровня триллиона транзакций и петабайта данных. У них всех узкое место — блокировки, и неконтролируемое расползание ошибок пользовательских алгоритмов по сотне таблиц (из тысячи штатных). Изменить схему хранения данных, поле добавить — выгоняй пользователей на час, и т.д. Мне нужна биг-дата ERP, а там не пользуются ни блокировками ни транзакциями, и довольно редко — SQL.
У них всех узкое место — блокировки

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

Это решается построением еще одного уровня абстракции над SQL. Но никак не переходом на NoSQL, где с этим может быть все еще хуже.
Изменить схему хранения данных, поле добавить — выгоняй пользователей на час, и т.д.

Вообще ЕМНИП СУБД позволяют очень много вещей делать не выгоняя пользователей. Не говоря уже о том, что динамическое изменение структуры по ходу и надежность — это противоположные вещи.
Мне нужна биг-дата ERP

Вообще у вас странная ERP. 24x7 (что уже редкость), с петабайтами данных и триллионами транзакций (что строго говоря для SQL на примитивной логике тоже не проблема, вспомните убер) и с логикой где не нужны выборки по условиям и целостность данных. Вы уверены что такие ERP существуют? И вообще в моем понимании ERP это именно что сложно-функциональные бизнес-приложения с высокими требованиями к целостности. А что это в вашем представлении?
Я с вами и не спорю. Статья не про то, что SQL плохо, а про схему разделения данных, и способ написания запросов. Проектик этот — чисто исследовательский, по результатам которого я кое-чему научился, и понял, что например могу миллиард записей обработать за минуты (а не часы), а на каком нибудь Rust и того быстрее, и что функциональные запросы не сложнее писать чем SQL, и так далее.

По последнему вопросу — я достаточно с ERP поработал, и с нашими, и с ненашими, нет там высоких требований к плотности транзакций — максимум что я видел в штатном режиме работы — это одна транзакция в секунду :) Больше только в режиме закрытия периода, а это обычно по ночам. У биллингов и финтеха намного больше, вот там нужны вообще in-memory СУБД. А данных в ERP накапливается много, аудируемость важна, защита от злонамеренного разработчика или админа. Неспроста придумали и старательно форсят функциональное программирование — оно не про скорость, оно про надежность и тестируемость кода.
Проектик этот — чисто исследовательский, по результатам которого я кое-чему научился, и понял, что например могу миллиард записей обработать за минуты (а не часы)

дурное дело нехитрое.
запустил select count(*) в ms sql с условием, не попадающим в индексы, на таблице с 200кк записей, в один поток — 24с. экстраполируем на миллиард записей — 2 минуты. если убрать option (maxdop 1) — будет в разы быстрее.

А у вас эти 200кк записей не в памяти (в shared_buffers или как они там в ms sql называется)? Потому как если вся таблица в памяти, там цифры куда меньше будут.
Неспроста придумали и старательно форсят функциональное программирование — оно не про скорость, оно про надежность и тестируемость кода.

А причем тут функциональное программирование? Это классная штука не спорю, но его можно одинаково «компилировать» как в SQL (что многие и делают) так и в NoSQL. И причин «компилировать» все в SQL куда больше, прежде всего из-за важных оптимизаций (с индексами и параллелизмом) и ACID из коробки.
Ни одна из известных мне систем не масштабируется до уровня триллиона транзакций и петабайта данных

а ваша система с fullscan на каждый чих масштабируется?


них всех узкое место — блокировки, и неконтролируемое расползание ошибок пользовательских алгоритмов по сотне таблиц (из тысячи штатных)

из статьи:


К примеру, триггер документа «продажа» делает следующее:

как тут без блокировок/транзакций решается проблема с race condition при одновременном обновлении баланса двумя пользовтелями?

Системы бигдаты построены на последовательных файлах и map / reduce, и таки да, они масштабируются до размеров, где ни одна РСУБД не встанет. И там таки фуллскан, с параллельностью (не все алгоритмы можно распараллелить в принципе). И никто не говорил, что блокировки и транзакции не нужны, просто статья не про это немного, она больше ответ на вопросы к первой статье, и про функциональное программирование :)
Почему это называется ЕРП?

Потому что это квинтэссенция 1С, JavaScript (простите, TypeScript) и Функционального Программирования. Иногда человек так упарывается в своих заблуждениях, что начинает считать труды поколений разрабов до него глупыми заблуждениями.

начинает считать труды поколений разрабов до него глупыми заблуждениями.
Вы что-то перепутали, я такого точно не говорил :)
А вы на труды этих поколений, видимо, молитесь, не вдаваясь в суть) на каждый бэк накатываете постгрес+пгкластер+елк_стек (разумеется, всё в докере, который на виртуалке), а сам проект на фреймворках, которые всё через рефлекшины делают… да?)
Каждый инструмент хорош там, где он выполняет именно то ради чего создавался. И чем он менее универсален, тем лучше. А «умные дяди» работают за деньги и клепают что им скажут (а говорят, по моему опыту, клепать «универсальное» с кучей нахлабучек на каждый случай жизни сферического коня в вакууме), так что сколько бы сумоист не тренировался, а в паркуре лучшим не станет ;) ИМХО
>> Почему это называется ЕРП?
Да вот то ж.
Крайне странные представления и о ERP, и о бигдате.
Какая-то исследовательская кривулька с фуллсканами, не тестированная даже на трех юзверях, вдруг стала продуктом уровня Enterprise (мы-то считали, что это когда хотя бы юзверей 100 стабильно годами работают в системе, и она не падает).
Вот что происходит, когда модные слова NoSQL, bigdata, функциональное программирование ложаться на неокрепшие мозги, и там в результате пермутаций рождается навязчивая идея, что все зло от SQL, хотя это наиболее эффективный инструмент для работы со структурированной информацией.

Хочется задать вопрос — а вот hadoop, spark, это у нас все же считается бигдатой, или только файлы с Json, которые парсятся TypeScript достойны носить это гордое имя?

Я к тому, что вот можно со скаловским DSL Quill написать строго в функциональном программировании (уж коли вам оно так нравится) с типобезопасностью и лямбдами что-то типа такого:

val q = quote {
for {
p < — query[Person]
a < — query[Address].join(a => a.fk == p.id)
c < — query[Company].leftJoin(c => c.zip == a.zip)
} yield (p,a,c)
}
ctx.run(q) //: List[(Person, Address, Option[Company])]
(отсюда github.com/getquill/quill)

А оно там само скомпилируется (о ужас!) в Spark SQL, выполнится на Hadoop кластере по простым паркетным файлам многотерабайтного объема, и никаких велосипедов изобретать не потребуется, достаточно просто выгрузить данные из обычной (настоящей) ERP в Hadoop.
Да, я в курсе про спарк, и вы таки правы.
PS
Хотя, на самом деле вы SQL на скале написали, семантика join, только со стрелочками. И никакой иммутабельности в помине.

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


Но вы, очевидно, под этим термином подразумеваете запрет на изменение данных, что легко можно организовать даже в 1С: Бухгалтерия т.н. закрытием периода, и отьемом прав работы в закрытом периоде, отьемом прав на редактирование справочников. Хотя в случае с выгрузкой, ваши данные в hadoop и так никто не тронет.
Но уж точно эта проблема не решается переходом с SQL на NoSQL, в результате чего рождается "бигдата" из многократно задублированной при денормализации информации.
Разве что можно сьездить на конференцию и сказать "Мы работаем с ПЕТАБАЙТОМ!!!", но и то когда будете рассказывать подробности, конфуз может выйти. )

Вы мегабайты с терабайтами или хотя бы гигабайтами не перепутали?
900 мегабайт данных должны работать за 0.1с на запрос на чайнике.
Не совсем понял цифры у вас количество балансов больше чем собственно документов?
Преимущество реляционного решения — вообще можно обойтись без хранения промежуточных остатков. Их можно моментально получить полным пересчетом. По таблице с числовыми полями даже mysql справится.
Кроме того ваша система ляжет на сложных репортах, уже проходил это на одном из проектов на CouchDb.
Пришлось переносить все на mssql
Так в любой ERP количество промежуточных данных на порядок больше количества документов. В 1C вообще остатки по периодам хранятся, в Аксапте например каждая строка порождает как минимум складскую проводку, проводку ГК (иногда нет), сопоставление, cтроку потребности, и т.д. Без промежуточных данных контролировать остатки было бы нереально. А вот отчеты, согласен, дешевле из документов, потому что все равно пользователя интересует расшифровка любого агрегата до строки документа.
Так в любой ERP количество промежуточных данных на порядок больше количества документов

С промежуточными данными вы явно преувеличиваете. В 1С остатки хранятся для промежуточных периодов (по умолчанию по дням ЕМНИП, а потом по месяцам) и то только потому что а) они пытаются аналитику за далекие периоды в оперативную базу положить б) у них все очень плохо с оптимизацией запросов (в частности predicate push down'ами) и не храни они промежуточные итоги, с производительностью все было бы куда хуже. Так что это не показатель.

Остальные системы в основном хранят только актуальные остатки, а дальше обратным счетом рассчитывают показатели на нужные даты.
в одноце промежуточные данные исторически хранятся потому что очень медленное хранилище.
у них все очень плохо с оптимизацией запросов (в частности predicate push down'ами)
Кстати непонятно почему, ведь там mssql.
Ну во-первых, у них кроме ms sql, еще файловая и postgresql есть. И даже если под ms sql нормально работает, а под postgresql и файловую ложится для них это не выход.

А, во-вторых, они весьма вольно обходятся с физмоделью / генерацией запросов (например в составных типах — эдаком суррогате наследования), вставляя туда OR'ы с CASE'ами, где даже MS SQL начинает конкретно чудить.
Одно время думал стать специалистом 1С но пообщавшись в кулуарах с топ-внедренцами вынес мнение (возможно и ошибочное), что 1C-ERP это лебединая песня, дальше они либо должны предложить что-то радикально новое, либо медленный закат. Аргументы — 1С упорно идет против тренда индустрии, продвигая свои уникальные регистры вместо обычных таблиц, бейсик вместо нормального ЯП, свой диалект SQL и т.д.
Все не так просто. Бизнес-приложения это отдельный рынок, живущий по своим законам. 1С в нем наделал много ошибок, но остальные наделали их тоже достаточно, плюс рынок очень консервативный и инертный.

Из тех кто удалось его хоть немного изменить, я знаю только Odoo — они как раз взяли что-то условно трендовое (python), навернули над ним свой фреймворк (на самом деле это самая важная часть, потому как синтаксис языка это дело второстепенное), сделали интерфейс более современным, ну и главное обеспечили хоть какую-то модульность и сделали маркетплэйс для разработчиков (аля отраслевок 1С). По итогу у них 16к модулей написанных сторонними разработчиками и уже 4 миллиона пользователей как у 1С. Но, надо понимать, что технологический потенциал Odoo ограничен, поэтому это строго говоря только малый бизнес (по западным меркам), ну и в принципе им даже на западе консолидировать рынок вряд ли удастся. Не говоря уже о том, чтобы сильно изменить уже частично консолидированный рынок в России (в тех сегментах куда они лезут).
Про рынок понятно, но интересна именно технологическая сторона. Питон начиная с 3.6 становится нормальным языком (я фанат статической типизации по количеству набитых шишек), и теперь Odoo становится интересным. Правда по одной вакансии на русскоязычную страну это так себе. Но у ADempiere и того нет. И почему технологические ограничения? Обычно так говорят, когда система не позволяет управлять сложностью. Вот Аксапта худо-бедно умеет — за счет наследования классов (теперь уже и таблиц), за счет слоев, и т.д. 1С не умеет, потому что модульность ограничена, наследования нет, язык примитивный. В питоне, теоретически, есть все средства для написания чего-то большого, я бы с удовольствием поработал на проекте Odoo, но ехать в Саратов пока не уверен что стоит :)
Обычно так говорят, когда система не позволяет управлять сложностью.

То что я видел, и по своему опыту с реально сложными логиками там будут проблемы, такие же как у условного 1С. А значит текущая их бизнес-модель на рынке среднего и крупного бизнеса не сработает. Модель 1С тоже не сработает, так как так консолидировать рынок они уже не смогут (рынок на западе уже в достаточно mature и конкурентном состоянии и нужно что-то более революционное).

Ну и еще раз питон (как сам язык) это небольшая часть всей платформы (если мы говорим про платформы). Не надо переоценивать его роль. Есть еще штук 10 различных аспектов, где Odoo в чем-то лучше, в чем-то хуже. То есть может даже Odoo и лучше 1С, но не в 10 раз, а значит превратить brownfield в greenfield вряд ли ему удастся.

PS: Например, если взять тоже наследование и полиморфизм в бизнес-приложениях ключевой момент там — поддержка полиморфизма в SQL слое, а это ни Odoo, ни 1С толком не удалось.
поддержка полиморфизма в SQL слое, а это ни Odoo, ни 1С толком не удалось.
Уже 2 статьи написал на эту тему, но информация пока не заходит :) Если абстрагироваться от всяких мелочей моего pet-проекта, то останется главное — функционально-ориентированное прикладное API, которое, в отличие от повсеместно используемого процедурного с вкраплением SQL — более масштабируемо в плане управления сложностью, хотя и менее производительно, ведь за все надо платить.
Да не, все понятно. И функциональные подходы в разработке бизнес-приложений, действительно очень крутая штука, проверено на личном опыте. К вам то главный вопрос в другом. Зачем вы в NoSQL то все это компилируете. Почему не в SQL?

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

Все остальное просто лишняя работа, бинарная сериализация или компиляция в SQL — у меня нет свободных ресурсов чтобы это все писать, поэтому беру самое тупое и простое — цикл по файлу. В реальном продукте конечно это будет сделано по-другому (и не мной), наверняка все должно храниться в РСУБД с индексами, но это имеет смысл делать только тогда, когда имеет смысл делать.
Для меня сейчас главное — понять насколько сложным и (не) понятным будет прикладной код для типовых алгоритмов типа расчета себестоимости в производстве или MRP, сколько промежуточных данных придется нагенерить, какие будут связи между этими данными, и т.д.

Вот тут есть пример. Хотя он переусложнен немного для производительности, можно его еще проще сделать.
Функциональщина предполагает работу с потоками данных, а SQL манипулирует стейтом, и это совершенно разные подходы.

В SQL как раз все четко разделено. За изменение стейта отвечает DML, за вычисление данных SELECT. И если таблицу рассматривать как поток данных на нее вполне отлично ложится функциональщина. Подходы как раз очень схожи. Посмотрите хотя бы на LINQ в .Net. Мы с lair много на эту тему спорили. Вот пример как это в .Net скажем сделано.
Эхх, сразу не понял кто вы :) Давно хотел почитать, но руки не доходили. Обещаю изучить и ответить по существу через день. Пока навскидку лишь могу сказать следующее — SQL предполагает random access к данным, то есть можно табличек накидать как попало, в любой запрос воткнуть join, и очень толстый и умный рантайм (СУБД) все оптимально выполнит. У меня была идея радикальней — чтобы получить результат на чистых итераторах, где нет RA, а максимум back(), нужно:
a) хранить данные специальным образом
б) придумать специальный «потоковый» DSL
в) реализовать продвинутый кэш
г) партиционирование потока + seek() в виде дополнительной плюшки
Честно говоря, понятия не имею, жизнеспособен такой подход или нет, наверняка исследования проводились, но я не нашел, на первый взгляд это дает возможность неограниченного масштабирования при сохранении рантайма простым. А значит диапазон применений — от аудита потока ЭСФ в масштабах страны, до рилтайм анализа сетевого трафика.
PS
Ушел читать про неочередной язык программирования…
Если говорить конкретно про масштабирование, то random access ему никак не мешает. Основная проблема масштабирования это ACID и теория распределенных транзакций. Та же Master-Slave репликация много где идет из коробки и позволяет достаточно эффективно решать проблему масштабируемости. Основной фокус в Master-Master. Некоторые СУБД пытаются решать эту проблему (например Оракл со своим RAC) но там не все так однозначно.

То что вы предлагаете это как раз увеличение производительности, за счет значительного упрощения и снижения оверхедов создаваемых SQL серверами (собственно это обычно и называют NoSQL). Но это подходит как раз для фейсбуков / инстаграммов с примитивной логикой, но очень большой нагрузкой. ERP это из другой оперы (я бы сказал противоположной). Вот Тут мы пытались все это немного систематизировать (насколько это в принципе возможно).
Если говорить конкретно про масштабирование, то random access ему никак не мешает.
Конечно мешает. Любой мьютекс на шаред-стейт приводит к ожиданиям потоков, а значит ограничивает параллелизм. Даже теорема есть специальная. Не от хорошей жизни придумали хаскель, где в принципе запрещен стейт, а производительность у него весьма впечатляет, по последним бенчам в блоге PsyHaSTe например.

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

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

Не совсем понял. Ну включите уровень изоляции READ UNCOMMITED и будут у вас потоки читать грязные данные (по принципу что есть то есть) практически без блокировок. То есть параллелизм будет неограничен.
хаскель, где в принципе запрещен стейт

Вот тут есть доля лукавства. Данные то он откуда то берет для вычислений, эти данные и есть state.

То есть если вам дать «фиксированную» SQL базу и политикой безопасности запретить DML (то есть разрешить только SELECT), вот вам и чистая функциональщина получится (тоже без state).
Опять же, если вы свои алгоритмы строите на итераторах без мутабельных таблиц — вам намного реже нужны транзакции, тем более распределенные, таким образом еще одно узкое место отпадает.

Но данные для вычислений откуда то должны браться.
PS
По предыдущей ссылке, респект, прочитал, но вы предлагаете языковое улучшение, а не смену парадигмы, поэтому ваш подход назвать функциональным можно с бОльшей натяжкой, чем мой.

А можно уточнить тогда что вы подразумеваете под функциональным подходом? Чистые функции? Но данные все равно должны как-то попадать в систему, для их использования. То есть если взять тот же SQL, то SELECT — чистые функции, DML — подносчик снарядов для этих чистых функций. Вы же кстати в курсе что SELECT полон по тьюрингу и на нем можно любые вычисления реализовать.

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

Под ФП я понимаю (это неканонично) прежде всего работу на потоках иммутабельных исходных данных с помощью ленивых итераторов и их композиции. В терминах ERP — вычисления на потоке первичных документов. Конечно, данные, то есть документы, где-то хранятся, но зная что они неизменные, мы можем много чего оптимизировать. В частности распараллелить свой алгоритм «ручками», а не надеяться на рантайм.

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

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

Еще раз, то что вычисление идет в памяти не панацея. Современные СУБД спокойно забирают всю базу в память и тоже там выполняют все достаточно быстро. Проблема, когда одно и то же действие выполняется очень часто и сотнями разных пользователей. Как я уже писал узкое место в современных ИС не память, а процессор. И тут индексы единственное что может спасти.
основная проблема в их обновлении при изменении данных от которых они зависят (инкрементальные обновления).
Если алгоритм оформлен как reduce() то вообще не проблема — появились новые данные, скармливаем редьюсу, и он досчитывает свой аккумулятор. Проблема, если алгоритм не ложится на reduce(), а представляет, например, SQL — тогда инкрементальные обновления это сложно, и нужна СУБД. Самый типичный отчет — сводная таблица, если в агератах функции типа sum(), эта штука легко досчитывается и даже параллелится, а если там count_distinct(), то досчитывается, но не параллелится, и т.д.
индексы единственное что может спасти.
Собственно, да, у меня это кэш, кончится тем, что положу его в какой-нибудь Mongo, или даже Postgres. А вот исходные документы скорее останутся в бинарных файлах, так как СУБД отдают курсоры медленней чем файловая система, хотя вроде у оракла своя FS, не тестил.
Если алгоритм оформлен как reduce() то вообще не проблема — появились новые данные, скармливаем редьюсу, и он досчитывает свой аккумулятор. Проблема, если алгоритм не ложится на reduce(), а представляет, например, SQL — тогда инкрементальные обновления это сложно, и нужна СУБД. Самый типичный отчет — сводная таблица, если в агератах функции типа sum(), эта штука легко досчитывается и даже параллелится, а если там count_distinct(), то досчитывается, но не параллелится, и т.д.

Ну то есть только GROUP SUM по сути. А есть еще GROUP LAST, PARTITION, RECURSION, не говоря уже просто о композициях(JOIN) и арифметических/логических операциях. Задача на самом деле очень нетривиальная, прежде всего архитектурно. У Microsoft с Oracle ее решить не получилось. У нас же на нее лет 7 минимум ушло. А поверьте на одном GROUP SUM в ERP вы далеко не уедете.
А вот исходные документы скорее останутся в бинарных файлах, так как СУБД отдают курсоры медленней чем файловая система

Это экономия на спичках. Сейчас память настолько дешевая, что можно хоть всю базу в shared_buffers держать.
Абсолютно верно, если данные лежат в стиле ООП — каждая сущность в отдельной таблице, связи p2p без ограничений, да еще по сложному ключу — нужен и сложный язык запросов. Преимущества — экономим место за счет нормализации. У меня идея ровно обратная — данные храним строго-упорядоченно, связи только назад и никогда вперед, любая коррекция это новый документ, и т.д. В этом случае нам нужны только простые свертки, оконные функции, и, возможно, вложенные reduce(). Приведите конкретный алгоритм ERP, который вы считаете сложным, я попробую его покрутить у себя.
PS
Вы в своем продукте замахнулись на гораздо большее — на универсальную СУБД, у меня задача проще — всего лишь ERP :)
Составьте расписание работ с учетом блокировки ресурсов и мощностей :)
Блокировка ресурса это надо понимать резервирование сырья? Про резервирование мощностей понятно, в простом случае когда производственные центры взаимозаменяемы, задача по крайней мере формализуется :)
ну, можно и так сказать
Преимущества — экономим место за счет нормализации

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

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

Ну то есть балансы вы предлагаете императивно обновлять, а не функционально. Как например тут: documentation.lsfusion.org/pages/viewpage.action?pageId=2228636
Где вы в ФП задаете остаток, а дальше платформа все сама разруливает.

То есть получается вы обеспечите функциональность в одном аспекте, а в остальных у вас будет такая же императивность как и везде.
receivedQuantity 'Суммарный приход' = GROUP SUM quantity(ReceiptDetail d) BY item(d), stock(receipt(d));
shippedQuantity 'Суммарный расход' = GROUP SUM quantity(ShipmentDetail d) BY item(d), stock(shipment(d));
currentBalance 'Текущий остаток' (Item i, Stock s) = receivedQuantity (i, s) (-) shippedQuantity (i, s);
Вы что, каждый раз рассчитываете остаток от рождества христова? Не верю. Значит остаток таки храните, а функциональщину обеспечиваете магическим рантаймом. Я о таком тоже думал, но передумал, ибо у меня хранятся все балансы на любой момент времени, и смысл функционалить тогда.
Есть магическая опция MATERIALIZED. Включаете ее и включается по сути ленивое вычисление. Само вычисление остатка при этом задается функционально. Вот в более классическом стиле с замыканием:

receivedQuantity 'Суммарный приход' (Item i, Stock s) = GROUP SUM quantity(ReceiptDetail d) IF item(d) = i AND
stock(receipt(d)) = s MATERIALIZED;

А вы балансы императивно по сути заполняете. То есть получается «функциональщина» у вас в каких-то отдельных аспектах только есть.
Так и есть, потому что у меня балансы создаются каждый раз заново, и материализовывать просто нечего. Это не бага, а фича — история остатков / резервов / потребностей (это все разновидности балансов), прибитая гвоздями иммутабельности, весьма полезна в планировании покрытий и разруливании конфликтов.
Я не про то. Сама логика остатка (что это такое) у вас декларативно (функционально) или императивно задается? Можете ссылку на код, где задается что остаток это сумма приходов — сумма расходов сгруппированные по товару / складу?
Все императивно в триггере. Генерация нового баланса — Приход, Расход, Перемещение. Конечно, в проде нужно писать удобное API, но в прототипе, для целей демонстрации — самое то.
Ну так у вас ключевые агрегации императивно задаются :) Причем тут тогда функциональная СУБД в заголовке? NoSQL да, но в такой постановке 1С с их регистрами функциональнее :)
ФП на уровне документов и бизнес-агрегатов, а в реализации конечно императивка. Если вы пишете например на Scala, у вас функция внешне чистая, но внутри нее вполне допустимы и мутабельные переменные, и циклы, мы же не параноики. В целом согласен, и заголовок и некоторые тезисы статьи немного провокационны, но не более чем у других :)
Ну провокации нужны, никто не спорит. Но не когда вы заявляете одно, а по факту получаете диаметрально противоположное :)

То есть у вас как раз «анти-функциональная» — императивная СУБД таким образом получится.
Я о таком тоже думал, но передумал, ибо у меня хранятся все балансы на любой момент времени

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

В иммутабельной части невозможно, а в мутабельной — при вставке задним числом — блокируется база, и последовательность документов пересоздается в памяти (удаляются старые баланcы начиная с точки изменения, новые пересоздаются по цепочке). Это если реальная правка задним числом (сторно). Если накладные расходы пришли в середине месяца — они просто дооценят текущие балансы, а старые останутся неизменными. То есть блокировка базы нужна будет далеко не всегда. На моем ноуте обработка миллиона документов в памяти — 0.6 секунды, на сервере время блокировки будет сотые. Конечно, если в вашей ERP открытый период — год, то моя схема вряд-ли подойдет :)
  1. "невозможно" — очень плохое слово, иногда нужно что-то менять в закрытом периоде;
  2. мне кажется, что с ростом числа учитываемых величин, ваша система рухнет под хранением промежуточных значений, в той же 1с сделано более разумно — сущности вроде остатков хранятся с какой-то периодичностью, достигается компромисс между компактностью хранения и лёгкостью пересчёта с одной стороны, и простотой расчёта значения на произвольный момент с другой.

ну и сама идея "всё в плоском файле без индексов" мне не близка, поиск движений по какой-то гравицапе, которых было три штуки за всё время, превращается в fullscan, отчёт по складу — аналогично; и т.д., и т.п..

1) Теоретически можно менять и в закрытом, но это перезапись большого куска базы, долго и блокирующе. Издержи подхода.

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

3) Если аналгогичный запрос движений по гравицапе уже был — запись будет в кэше, и ее достаточно актуализировать на новых документах. Если нас интересует нестандартная свертка (какой еще не запрашивали), но без сальдо и по конкретному периоду (например обороты за прошлый год) — индексировать плоский файл по timestamp — плевое дело. Если фильтр не по времени — тогда извините, фуллскан.
PS
В целом я не против ни СУБД ни индексов, для прототипа выбрал файл только пожалев свое время, но все же хочется минимизировать мутабельность, то есть неконтролируемая правка любых записей в любой момент. В западных ERP к этому прикладывают специальные усилия, но их недостаточно.

2 — в 1с есть "пересчёт регистров", именно про это самое

Нет, скорее «перепроведение документов» про это самое. Пересчет регистров это все же немного из другой оперы (хотя наверное какая-то корреляция все же есть)
Как я уже писал узкое место в современных ИС не память, а процессор
Вы уверены..? Зачем тогда bson/varint и т.п.? Вроде как процессор как раз самое быстрое место компьютера, потом ОЗУ, за ним сеть и ПЗУ (они разные бывают, так что х.з. кого на первое место поставить)
Плюс, такая тема как избыточное копирование данных между буферами (которым страдает Линукс и не страдает, вроде как, бсд)
Вы же кстати в курсе что SELECT полон по тьюрингу и на нем можно любые вычисления реализовать.
а «добавить» строки в возвращаемый результат — нельзя( в итоге либо встраиваемые процедуры писать (я не уверен, позволяют ли они это — решил вопрос вторым способом), либо на бэке это делать (в этом случае дополнительная задержка ввиду меньшей параллельности выполняемых задач)
Хранимка + временная таблица, теперь даже в 1С так делают :)
А что насчёт CTE? Они вроде как эффективнее временных таблиц, однако… я плохо знаю тему чтобы сравнивать дальше — поделитесь опытом (если имеете таковой), плиз (:
Так CTE оно для рекурсии придумано, произвольно обновить часть записей именованного курсора вы все равно не сможете. У меня давно еще была задача расчета плановой себестоимости по дереву BOM с учетом потерь и отходов на чистом SQL. Никакие синтаксические изыски этого тьюринг-полного языка не позволили избежать временных таблиц. Возможно лично я и ошибаюсь, но не от хорошей жизни процедурные расширения добвили во все СУБД.
Все это зависит от модели предметной области.
Допустим, если процесс организован как {Входы, Процессоры, Выходы},
то стоимость Процесса = Стоимость Входов + (Износ Процессоров + Расход ТМЦ)
а стоимость конкретного Выхода = Стоимость Процесса / (Количество Выхода * Коэффициент распределения)
Если предположить, что Потребность любого Процесса клиента удовлетворяется Процессов сервером, который поставляет некоторое количество своих Выходов во Входы Процесса клиента, то цепочка формирования стоимости становится тривиальной структурой и обрабатывается алгоритм в три строчки кода. При этом учитывая, что каждый Процесс запущен (или спланирован) в контексте спецификации производственного заказа, производственного заказа, заказа, контракта, то легко вычислить все соответствующие показатели, вплоть до перераспределения затрат и т.д…
Хотя все эти расчеты нафиг не уперлись, так как прибыль никак не зависит от наличия и/или точности этих расчетов, а цену определяет рынок и /или административный ресурс.
Это все к тому, что парадигмы программирования и т.д. белиберда к реальным задачам бизнеса отношения не имеет.
В целом соглашусь что бизнес-алгоритмы просты, но всеж бывают ньюансы. Например:
— Зацикливание графа сопоставлений при наличии возвратных отходов или реклассификации готового продукта в полуфабрикат.
— Дооценки сырья сильно задним числом (когда продукцию произвели и уже продали).
— Коррекция потребностей, уже принятых в работу отделом закупок (купили, а это никому не нужно).
— Коррекция планов производства внутри декады по причине форсмажора.
— Управление резервами по приоритетам (автоматически снять резерв с менее важного, и отдать более важному).
Это все может рекурсивно затрагивать старые данные, и наш принцип иммутабельность будет нарушаться. В классических ERP проще — я там могу в старых записях дозаполнить новые поля, а с журналом документов мне придется любую коррекцию чего-нибудь (да хоть резерва) оформлять отдельным документом. Я пока масштаб бедствия до конца не осознал, видимо нужно придумать какой-то радикально-сложный пример, может Andrew-BUSINESS имеет что-то готовое, либо придется самому ползти медленно и постепенно.
balajahe

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

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

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

3. Дальше этот граф «раскручивается» — решается последовательность уравнений, в которых могут быть и циклы (системы уравнений).

Не знаю, это как-то поможет понять как решать те проблемы, которые Вы перечислили?
Исчерпывающе!
Остается смоделировать это на функциональной СУБД и понять, насколько она годится для этой задачи:)
Попробую, но все-таки наверное сначала резервы, потом планирование, и только потом учет. Учет штука более однозначная, в планировании же куча трудно-формализуемых вещей, на чем проекты ERP и ломаются. В планировании откат в прошлое это почти норма, а в учете это стихийное бедствие.
Планирование очень сложное, да.
Отходы — обычные Выходы (обычно Процессы потребители этих выходов вне контекста Производства — это утилизация и т.д., хотя он могут быть и переработаны).
Все эти «классификации» аспектные, любой набор свойств может классифицирован множественно и одновременно и при том динамически. Так что проблем чего то переклассифицировать стоит в системах с единственной «верной» классификацией (типа буховской) на все времена и случаи.
ДоПереОценка и т.д. — виртуальные операции для некоторой отчетности, по сути нафи никому не нужные.
Отдел закупок не может закупать что благорассудится, они исполнители процессов (хранения, перемещения) сгенерированных системой планирования процессов.
Перераспределение резервов происходит автоматически по заданным Политикам потребления заделов системой планирования процессов.
Управление — реакция на изменения стейта :) происходит автоматически по заданным правилам и при нарушении определенных ограничений.
Все эти документы и т.д. — некое представление Процесса (т.е. структурно все это описано как Процесс), потому ничего отдельно оформлять не надо.
Ух ты, почти все раскритиковал, негоже это воще то.
Что бы не удариться во все тяжкое надо почитать про вещи попроще, типа TPL DataFlow. habr.com/ru/post/138531
в моей системе нет вообще никаких промежуточных данных и все нормально. Вы как то слишком все усложнили — проводка по складу это просто запись об изменении количества в ту или иную сторону. Каким боком там вообще всякие потребности и прочее.
остутствие промежуточных остатков позволяет простым запросом высчитывать обороты и остатчки и не заглядывать на какую дату каки промежуточные там данные. представте что у вас промежуточный итог на первое января мне надо посмотреть отчет за период с середины декабря по середину января. Могу я просто послать к БД запрос? в вашем случае врядли.
Кроме того это позволяет легко проводить документ=нты любым числом хоть задним хоть передним без пересчета остатков. прибил записи связаные с документом и добавил новые и все.
Я к тому что при нынешней копеечной стоимости железа так усложнять хранилище это экономия на спичках. И никакие nosql пока не переплюнут реляционные БД для учетных систем где данные реляционные по своей природе.
Кроме того это позволяет легко проводить документ=нты любым числом хоть задним хоть передним без пересчета остатков. прибил записи связаные с документом и добавил новые и все.
Нельзя этого делать. Как только вы добавите планирование, вы поймете почему. Потребности нельзя вычислять на лету, их нужно фиксировать с определенной периодичностью (например не чаще раза в месяц). Иначе ваш отдел закупок с ума сойдет от такого динамизма.

Вы изобретаете каппа архитектуру. Рекомендую поликбезить сабж.

Респект, уже :)
Супер!
Спасибо, что оперативно выпустили эту статью, ответив таким образом на мой комментарий.

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

Буду стараться следить дальше.
Если вдруг нужна будет помощь по методологической части (резервы, себестоимость, цепи поставок) — можете обращаться.
Спасибо, обращусь за помощью как дойдем до планирования!
Если раньше не брошу эту неприбыльную затею :)
У меня самого есть идея запилить пост о новой бизнес-логике для ERP.
Возможно, я заставлю себя, чтобы руки дошли до него чуть быстрее.
NitroJunkie, по поводу вычисления остатков — отвечу развернуто. В ERP нам нужен граф сопоставлений — и для себестоимости, и для планирования. Есть 2 способа организации сопоставлений:
1) проводка-проводка
2) проводка-остаток
Если мы выбираем первый случай, тогда мы можем расчет остатка оформить вычисляемой кэшируемой сверткой, в чисто-функциональном ключе. Я сознательно выбрал второй вариант «проводка — остаток», таким образом остаток нам нужен в системе как самостоятельная сущность, с уникальным ID. Какая может быть функциональщина при организации связей между сущностями?

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

Вообще чистая функция (а ФП это именно про чистые функции, потому как не чистые функции это просто процедуры, которые умеют записывать результат в переменные) никак не может возвращать новый документ. Потому как при повторном вызове она получается вернет другой документ, что противоречит ее чистоте. Это никак не ФП.
Чувствуете разницу?

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

Функциональная функция (простите за тафталогизм) всегда возврщает новые данные, поэтому ФП так критикуем и непопулярен, ведь аллокации памяти это дорого. А если фунция ничего не возвращает, и не меняет старых данных, то это не функция вообще, а черная дыра :)

По сути вы просто создаете транзакционный лог. Я бы на вашем месте это скорее блокчейн СУБД, а не функциональной СУБД называл бы. Ведь именно блокчейн это про аудируемость и т.п.
Именно! Вы совершенно верно схватили суть! На такую схему навернуть блокчейн — пять минут программирования, просто вычислить хэш и положить в дерево Меркла. А вот если у нас есть мутабельный регистр «Остатки», то как его надежно защитить от подделок и ошибок? Никак.
Функциональная функция (простите за тафталогизм) всегда возврщает новые данные, поэтому ФП так критикуем и непопулярен, ведь аллокации памяти это дорого.

С чего вы взяли? ФП вообще перпендикулярен работе с памятью (то есть можно по разному делать). И как раз в ФП экономить память (и ее аллокацию) куда проще чем в императивном: а) из-за ленивых вычислений, б) потому что вы можете переиспользовать ссылки на объекты не боясь что их кто-то изменит (если у вас вся архитектура на immutable объектах / чистых функциях, соответственно immutable объекты создаются только в императивной части — процедурах). И кстати поэтому ФП очень популярен именно в системном программировании, потому как на сложных архитектурах при использовании ФП с многопоточностью и памятью (кэшированием) все становится значительно проще.
Именно! Вы совершенно верно схватили суть! На такую схему навернуть блокчейн — пять минут программирования, просто вычислить хэш и положить в дерево Меркла. А вот если у нас есть мутабельный регистр «Остатки», то как его надежно защитить от подделок и ошибок? Никак.

Ну насколько хорош или плох блокчейн это отдельная тема. И находится она в основном в политическо/экономической плоскости, так что к данной статье имеет мало отношения :)
Ну не знаю. Бывает, что сама задача хорошо ложится на ФП, но чаще нужно специальным образом писать чтобы «переиспользовать ссылки на объекты не боясь что их кто-то изменит». В реальной жизни все хуже, я вот недавно делал бенчмарк, на котором Scala умерла, а комментаторы указали, что я выбрал неправильный алгоритм. То есть ФП работает только на правильных алгоритмах :) Я сколько не пробовал — суровая императивщина с указателями на кучу и циклами получалась быстрее всяких итераторов. А вот применительно к БД — подход уже интереснее, потому что в БД накладные расходы на изменение старой записи соизмеримы с накладными расходами на добавление новой. Другими словами, функциональщина в БД обходится дешевле чем в ОС.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории