Нормализация данных в распределенной БД, микросервисах и ERP +5


Привет, Хабр!

Эта маленькая заметка родилась в процессе обсуждения статьи «Распределенные монолиты...», а поскольку тема требует дальнейшего раздумья — я решил зафиксировать ее у себя в блоге. Автор статьи фактически описывает распределенную базу данных, доказывая, что единственнo правильной структурой хранения в ней является Журнал Событий. Аргументы приблизительно следующие:

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

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

Типичное бизнес-приложение можно представить в виде цепочки трансформации данных:
«входные данные => модель => выходные данные». Любая схема хранения — это компромисс между тремя крайностями:

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

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

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

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

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




К сожалению, не доступен сервер mySQL