В наши дни NoSQL продолжает набирать популярность, но мало кто знает, что нереляционные СУБД появились гораздо раньше даже самой реляционной алгебры. 40 и даже 50 лет назад в первичном «бульоне» зарождающейся IT индустрии «варились» только NoSQL-продукты. И что самое интересное – продукты, рожденные в те сложные времена, живы до сих пор и прекрасно себя чувствуют.
Одним из таких продуктов стала СУБД GT.m, разработанная компанией Graystone Tehnologies в 70-80-х годах прошлого века. СУБД нашла широкое применение в медицине, страховании и банковской сфере.

В нашем банке мы тоже используем GT.m, и этот инструмент прекрасно справляется с обработкой большого количества транзакций. Но… Есть одна проблема: GT.m никакой для аналитики, в нем нет SQL, аналитических запросов и всего того, что делает финансового аналитика счастливым. Поэтому мы разработали собственный «велосипед» для репликации данных из GT.m в «реляционные» СУБД.


А вот здесь должна была быть картинка с летающим велосипедом

Всех заинтересованных приглашаем под кат.

Псс… Не хочешь еще немного GT.m? Уже в те доисторические времена GT.m имела (или имел) подержу ACID, сериализацию транзакций и наличие индексов и процедурного языка MUMPS. Кстати, MUMPS – это не просто язык, это целое направление, появившееся еще в 60-х годах 20 века!

Одной из самых успешных и популярных MUMPS-based СУБД стала Cache, и вы, скорее всего, слышали о ней.

Основой MUMPS СУБД являются иерархические структуры – глобалы. Представьте JSON, XML или структуру папок и файлов на вашем компьютере – примерно то же самое. И всем этим наши отцы и деды наслаждались до того, как это стало мейнстримом.
Один важный момент – в 2000 году СУБД стала Open Source.

Так вот, старушка GT.m надежна и, несмотря на свои преклонные года, обслуживает большое количество специфичных транзакций без каких-либо усилий в отличие, например, от своих SQL собратьев (фраза, конечно, холиварная, но для нас это факт: на определенной нагрузке NoSQL все же шустрее SQL). Однако все проблемы начинаются тогда, когда нам нужно сделать простейшую аналитику, передать данные в аналитические системы или, не дай бог, автоматизировать все это.

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


Решение с выгрузками

ODS (Operational Data Store)


У нас уже был реализованный архитектурный паттерн под названием ODS (Operational Data Store), в который мы реплицируем наши источники с минимальными отставаниями от 2 секунд до 15 минут.

Данные из ODS мы загружаем в хранилище данных (DWH) либо строим по ним оперативные отчеты. И с реляционными СУБД типа Oracle, MS SQL, Sybase и т.д. нет проблем – грузим таблицы источников в те же самые таблицы на ODS.



Мы очень хотели реализовать подобную репликацию GT.m в ODS.
Но как же грузить NoSQL структуры в простые таблицы? Как, например, загрузить клиента, у которого может быть 2 телефона, а может и 22?



Мы понимали, что правильнее будет организовать репликацию на основе бинарных логов СУБД (в других СУБД они называются WAL, Redo, transaction log и т.п.), благо, GT.m журналирует каждую транзакцию, изменяемую данные. При этом на рынке уже существовали готовые продукты, одним из которых является Evolve Replicator от компании CAV systems.

Evolve CAV systems


Evolve читает журналы транзакций, трансформирует их и записывает строки в таблицы уже на реляционном приемнике.

Но была одна совсем маленькая проблема – это решение нам не подходило… В наших структурах имелось большое количество вычисляемых значений (Computed Data Items или CDI).

Попробую объяснить на пальцах. Это чем-то напоминает «виртуальное поле» в таких СУБД, как Oracle, в которых значение не хранится, а вычисляется на момент обращения к этому полю. При этом CDI может иметь достаточно сложную логику и базироваться на данных из дочерних узлов и т.д. И, как вы наверно уже догадались, такие Computed Data Items невозможно реплицировать из журналов СУБД, так как информация по ним там не хранится, потому что в журналы записываются только изменения физических полей. Но такие поля-призраки нам очень нужны для аналитики, в них сложная логика, и иметь аналитическую реплику без этих полей было бы бессмысленно.

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



FIS Profile


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

Таким приложением является FIS Profile (далее Profile) – это автоматизированная банковская система, полностью интегрированная с GT.m. Кроме функций автоматизации банковской деятельности, Profile обеспечивает следующий функционал:
1. Простейший SQL (select * from table where id=1)
2. Доступ к данным по JDBC
3. Представление глобалов в табличный вид, при этом один глобал может быть представлен в несколько разных таблиц
4. Триггеры
5. Секьюрность
По сути, мы имеем еще одну СУБД поверх другой СУБД. При этом одна из них будет реляционной, а другая – NoSQL.

Profile является полностью проприетарным ПО, но есть и Open Source аналоги, например, Vista Fileman.


Логические уровни нашей GT.m-системы.

Реализация концепции


Для репликации NoSQL-структур данных в SQL СУБД в первую очередь необходимо:

1. Представить глобалы в табличном виде.
При этом один узел «дерева» может быть представлен в виде нескольких, связанных между собой таблиц. Такую возможность уже предоставляет Profile, и все, что нам необходимо, – это правильно настроить такие табличные представления. Задача хоть и сложная, но вполне решаемая.

2. Захват изменений.
К сожалению, наличие CDI в нашей системе не позволяет сделать «правильную репликацию» из журналов СУБД. Единственный возможный вариант – логическая репликация триггерами. Изменилось значение в таблице – триггер это отловил и записал изменение в журнальную таблицу. Кстати, журнальная таблица – это тот же самый глобал. Сейчас все сами увидите!

Вот так выглядит типичный глобал:



Понимаем, выглядит как минимум… странно, но в те далекие годы понятия красоты были совершенно другими. Структура глобала также называется «многомерным разреженным массивом». И ключ – это как бы координата данных, которые в нем лежат.
Кстати, по «данным» можно также строить индексы, что бывает очень удобно для табличного представления.

Собственно, из такого глобала мы можем получить 2 таблицы:

TABLE_HEADER:



TABLE_SHED — лог изменений:



Кстати, числовые значения преобразовались в даты, например, для поля TJD.

По имеющимся таблицам выполняется запрос.


где:
:STARTPOINT – дата последнего запуска;
'Т' – текущая дата (выглядит как минимум странно, но эта функция – аналог sysdate() или now() в нормальных других СУБД)

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

3. Выборка данных из журнальных таблиц и последующая их передача в ODS.
Существовавший на тот момент данных JDBC-драйвер прекрасно работал с атомарными запросами, но вызывал утечки памяти во время массированных операций Select. Имеющийся драйвер пришлось значительно переписать.

4. Доставка и применение изменений.
Очень важный аспект – быстрое применение данных на приемнике. Если GT.m успешно справляется с большим количеством атомарных транзакций, то для реляционных СУБД типа Oracle это несет большую нагрузку. При этом в наш ODS льются данные из большого количества других источников (всего около 15).
Для решения этой проблем, необходимо собирать все такие операции в пачки и применять их группой. Такие операции называются Bulk и полностью зависят от специфики СУБД приемника.


Текущая архитектура репликации

Наше приложение – кстати, мы его назвали Profile Loader – загружает в ODS два типа таблиц: журнальные и зеркальные. Мы постараемся рассказать про ODS в будущих статьях, но если кратко, то:
журнальные таблицы – таблицы логов изменений, эти таблицы удобны для инкрементальной загрузки, например, в аналитические системы и DWH
зеркальные таблицы – таблицы, содержащие в себе полную копию данных источника, такие таблицы удобно использовать для аудита и для оперативной аналитики.

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

Задачи, решаемые SQL репликой


1. Избавление от разрозненных выгрузок. Мы получили единое окно для всех потребителей данных.
2. Аудит. Упрощается процедура аудита за счет того, что данные лежат в удобном виде, а мощь SQL позволяет удобно и быстро этими данными оперировать.
3. Качество данных. Например, в GT.m всего 2 типа данных – числовой и строковый. Когда данные прилетают в ODS, они преобразуется в другие типы, в том числе и в даты. Если дата в неправильном формате, мы можем легко отлавливать такой инцидент и улучшать качество данных уже на источнике.
4. Вычисление инкремента для дальнейшей загрузки в DWH.

Дальнейшие пути развития


На будущее планируем реализовать следующее:
1. Полностью избавиться от существующих CSV-выгрузок. Сейчас они все еще живы, но мы их будем потихоньку «отстреливать».
2. Оптимизировать некоторые проблемы с производительностью.
3. Поделиться идеями с заинтересованным сообществом, возможно и поддерживать проект в OpenSource.
4. Попробовать интеграцию с Oracle GoldenGate на уровне доставки изменений.
5. Возможно, сделать обратную реплику (дополнительную, не ODS) Replica -> GT.m, для сервисных процессов повышения качества данных.
6. Развивать оперативную отчетность поверх ODS.

Резюме


В статье мы рассказали о нашем детище – Profile Loader и о том, как NoSQL данные можно анализировать в SQL СУБД. Данное решение возможно не совсем правильное и элегантное, но оно прекрасно работает и выполняет возложенные на него обязательства.

Если вы решитесь реплицировать свою NoSQL БД в «реляционку» для удобной аналитики, в первую очередь оцените объемы изменений, модель данных и возможности тех технологий, которые все это будут обеспечивать.

Желаем успехов в ваших начинаниях!
Всегда готовы ответить на ваши вопросы.

P.S. Также выражаем благодарность коллегам, участвовавшим и активно помогавшим в проекте: Шевелеву Дмитрию, Чебанову Николаю, Бубону Роману, Быстрову Денису, Бейспекову Кайсару, Люфко Андрею, Кудюрову Павлу, Воробьёву Сергею, Лысых Сергею, Кулешову Денису, Никитчик Елене, Мушкет Ольге, Чечёткиной Юлии, Пасынкову Юрию и коллегам из CAV Systems и FIS.
Поделиться с друзьями
-->

Комментарии (31)


  1. Kore
    03.08.2016 09:35
    +1

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


    1. yusman
      03.08.2016 09:55

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


      1. Kore
        03.08.2016 10:25

        Спасибо.
        Вы только задумываетесь о распространении вашего решения через OpenSource коммьюнити? Или уже занимаетесь этим?


        1. yusman
          03.08.2016 11:46

          Задумываемся, как только найдем некоммерческий проект на GT.m и заинтересованность с их стороны — обзательно пойдем в Open Source.


  1. kefirfromperm
    03.08.2016 09:54

    Назвал иерархические СУБД NoSQL и всё сразу стало стильно модно молодежно.

    Вряд ли корректно называть иерархические СУБД NoSQL, что изначально означало Not Only SQL. Что как бы говорит о следующем шаге, о дополнительных возможностях и снятии ограничений наложенных реляционной моделью. Унаследованные иерархические СУБД — это, всё же, немного не то. Да и класс NoSQL гораздо шире.


    1. yusman
      03.08.2016 10:11
      +1

      Спорить конечно на эту тему можно бесконечно, но по факту в современных NoSQL СУБД структуры примерно такие же, что и в старых, только названы по другому — JSON, Objects store и т.д… Что-то дает больше удобства, что-то большей гибкости, но всегда находится компропис и новые проблемы. История циклична.
      Мы постарались описать только подход, его можно попробовать применить и на другие NoSQL СУБД, на какие конкретно — совершенно не важно, буть то Mongo или, например, DocumentDB.


    1. Delphinum
      03.08.2016 10:24
      +2

      Назвал иерархические СУБД NoSQL и всё сразу стало стильно модно молодежно

      NoSQL это не только иерархические СУБД. Тот же Redis можно назвать NoSQL, что не сделает его иерархической СУБД. Если мы конечно не будем вдаваться в демагогию и называть СУБД типа Ключ-Значение частным случаем иерархической СУБД с одним уровнем вложенности.


      1. kefirfromperm
        03.08.2016 12:41

        Я о том и говорю, что автор вместо использования термина иерархические СУБД использует NoSQL. И это не совсем корректно.


    1. adaptun
      03.08.2016 16:07
      +3

      NoSQL изначально означало «No SQL». Это уже потом появились альтернативные расшифровки, вроде «Not only SQL», «No! SQL» и так далее.

      Вот, например, Мартин Фаулер в 2012 году предлагает расшифровку «Not only SQL», сравнивая с давно устоявшейся «No SQL».
      http://martinfowler.com/bliki/NosqlDefinition.html.

      Там же Фаулер приводит общие характеристики таких NoSQL СУБД:

      1. Not using the relational model (nor the SQL language)
      2. Open source
      3. Designed to run on large clusters
      4. Based on the needs of 21st century web properties
      5. No schema, allowing fields to be added to any record without controls


      Как минимум 1,2 и 5 условиям GT.M удовлетворяет. Так что, по-моему, вполне корректно называть GT.M NoSQL СУБД.

      Фаулер, конечно, не истина в последней инстанции.


  1. Ivan22
    03.08.2016 10:22
    +1

    Логическая репликация на триггерах — ок. А нету вот такого же, но с перламутровыми для хадупа?


    1. yuryemeliyanov
      03.08.2016 18:46

      «В» Hadoop или «Из» Hadoop? :)


      1. Ivan22
        04.08.2016 08:44

        из


        1. yuryemeliyanov
          04.08.2016 11:00

          А куда? А смысл?


  1. G-M-A-X
    03.08.2016 11:41
    -2

    1. А смысл в этой легаси GT.m?
    2. Вы (впрочем, как и большинство) путаете SQL и реляционность. :)
    3. Какой вообще смысл в т.н. NoSQL? Это все равно, что в mysql будет 2 поля: первичный ключ и каша из данных. Выборка по первичному ключу и вставки, так как больше нету индексов, должны летать. :)
    Или я в чем-то ошибаюсь?


    1. yusman
      03.08.2016 12:23
      +2

      1. А смысл в этой легаси GT.m?

      Смысл в надежности.(за 8 лет не было серьезных сбоев) и быстроте продукта

      2. Вы (впрочем, как и большинство) путаете SQL и реляционность. :)

      Уточните пожалуйста, что было перепутано?

      3. Какой вообще смысл в т.н. NoSQL? Это все равно, что в mysql будет 2 поля: первичный ключ и каша из данных. Выборка по первичному ключу и вставки, так как больше нету индексов, должны летать. :)

      И да и нет. Такие иерархические NoSQL-структуры хороши тем, что можно, например создавать индексы прямо в «поддеревьях», так как не все задачи сводятся с простому получению значения по ключу, присутствуют так же массированные операции.


      1. G-M-A-X
        03.08.2016 13:33

        >Смысл в надежности.(за 8 лет не было серьезных сбоев) и быстроте продукта

        Как это достигается? Транзакции пишутся сразу? У меня за 8 лет тоже сбоев не было на mysql. Правда, нагрузок тоже.

        >Уточните пожалуйста, что было перепутано?

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

        Вот Сфинкс имеет несколько интерфейсов, в том числе и SQL.

        >например создавать индексы прямо в «поддеревьях»

        А обновить только один ключ в каше можно? :) Или нужно перезаписывать всю кашу?
        Такие индексы будут тупить при обновлении?
        Ну и на сколько они эффективны по сравнению с индексом по полю?


        1. yusman
          03.08.2016 15:49
          +1

          Как это достигается? Транзакции пишутся сразу? У меня за 8 лет тоже сбоев не было на mysql. Правда, нагрузок тоже.

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

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

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

          А обновить только один ключ в каше можно? :) Или нужно перезаписывать всю кашу?
          Такие индексы будут тупить при обновлении?
          Ну и на сколько они эффективны по сравнению с индексом по полю?

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


          1. G-M-A-X
            03.08.2016 16:40
            +1

            Как правило, SQL натянутый на нереляционные структуры всегда не полноценный

            Это уже второй вопрос.
            Если движок не позволяет делать группировки/джойны/другое, то это не вина SQL. :)

            Это всего лишь язык запросов. :)
            Это не сама СУБД.

            Это примерно как в чай в большистве случаев добавляют сахар.
            Но это не значит, что чай равняется сахару :)


    1. Kane
      03.08.2016 23:07
      +1

      Какой вообще смысл в т.н. NoSQL? Это все равно, что в mysql будет 2 поля: первичный ключ и каша из данных. Выборка по первичному ключу и вставки, так как больше нету индексов, должны летать. :)
      Или я в чем-то ошибаюсь?

      У разных NoSQL БД есть свои особенности, которых может не быть в SQL базах данных. Например, в Redis есть тип данных сортированное множество (sorted set, zset), позволяющий решать специфичные задачи (например, кэширование) или HyperLogLog — вероятностная структура данных, потребляющая фиксированное количество памяти для подсчёта количества уникальных элементов.


      Не нужно думать о NoSQL как о чём-то, что нужно использовать вместо SQL баз данных. Их можно использовать вместе.


  1. G-M-A-X
    03.08.2016 16:01

    Промазал веткой почему-то :)


  1. morisson
    03.08.2016 17:55
    +3

    А не глядели в сторону InterSystems Cache? Ведь, все, что крутится на gt.m, заведется сразу и на Cache?
    Есть асинхронное зеркало, для реплики всего на сервер аналитики, без нагрузки на OLTP.
    Есть SQLStorage Mapping для «натягивания» SQL- доступа на глобалы, но можно так же из журнала читать.
    Есть наконец встроенная BI InterSystems DeepSee MDX based?


    1. yuryemeliyanov
      03.08.2016 18:48
      +1

      Мне кажется все выше перечисленное нарушает концепцию ребят в части ODS (на Oracle).


      1. morisson
        03.08.2016 23:14

        А можно детально, что именно нарушает?
        В вышеперечисленном необязательно использовать все. Для ODS как раз подойдет асинхронное зеркало и/или SQLStorage Mapping.
        А с DeepSee не нужен и ODS (что, конечно, «нарушает»)


        1. yuryemeliyanov
          04.08.2016 00:04

          Асинхронное зеркало и/или SQL Storage Mapping умеет реплицировать в Oracle?


          1. morisson
            04.08.2016 02:01

            Асинхронное зеркало позволит создать квази-копию OLTP сервера, которую можно нагружать для:
            1. SQL Storage Mapping и работу с данными через JDBC/ODBC из Oracle или чего угодно еще и/или
            2. Работы с данными OLTP-сервера для любых ETL операций без риска загрузить/«сломать» OLTP.


            1. yuryemeliyanov
              04.08.2016 10:59

              ODS на Oracle подразумевает единую БД. Шаг перегрузки данных из, как вы говорите, квази-копии в Oracle тоже надо будет реализовывать. Но сама как идея у вас интересная.


  1. yuryemeliyanov
    03.08.2016 18:57

    Спасибо за статью! Интересно.

    Пара вопросов.
    — Не понял про триггеры. Получается что триггеры можно вешать на CDI?
    — Иерархические ну или объектные СУБД удобны «отсутствием» схемы данных (ваш пример про 2 и 22 телефона). Как в этом случае формируется структура таблицы на стороне ODS? И поддерживаете или же думаете реализовывать поддержку репликации DDL? А может это вопрос как раз к механизму создания таблиц аудита на стороне Profile.


    1. yusman
      04.08.2016 10:36
      +1

      Не понял про триггеры. Получается что триггеры можно вешать на CDI?

      Триггеры вешаются только на «физические» значения, но в момент срабатывания этих триггеров вычитаются и CDI.

      поддерживаете или же думаете реализовывать поддержку репликации DDL? А может это вопрос как раз к механизму создания таблиц аудита на стороне Profile.

      С репликацией DDL все сложно, во-первых GT.m не контролирует структуры глобалов, во-вторых иногда приходится думать как разложить структуры правильно.
      Иерархические ну или объектные СУБД удобны «отсутствием» схемы данных (ваш пример про 2 и 22 телефона). Как в этом случае формируется структура таблицы на стороне ODS?

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


      1. yuryemeliyanov
        04.08.2016 10:54

        В части триггеров, кажется понял. Т.е. CDI получаете уже тогда когда дельту вычитываете?

        И ещё. Не сочтите за придирку. Но, можно ли назвать то, что у вас получилось репликацией? По сути у вас просто получился ETL из NoSQL источника (GT.m) в ODS (Oracle)?


        1. yusman
          04.08.2016 11:27

          Поправка, результаты CDI не сохраняются в GT.m, они вычисляются во время забора изменений. Но в момент чтения данные в CDI будут консистентны.

          Да, можно это и назвать ETL(так как имеем Extract — transform — Load). Но можно и репликацию средствами Oracle GoldenGate или Attunity назвать ETL процессом с натяжкой))) Но почему «репликация»? В будущем мы будем увеличивать «риалтаймовость» системы и надеимся довести ее до нескольких секунд. Сейчас это несколько минут.


          1. yuryemeliyanov
            04.08.2016 11:38

            Трансформации в Oracle GoldenGate или Attunity это скорее возможность, у вас же трансформации — необходимость :) Но в случае NoSql по другому, я думаю, и быть не может.

            Можете потом свои наработки продать Informatica, они назовут это модным словом адаптер (PowerExchange for GT.m) :)

            «Риалтаймовость» — это круто! Желаю успехов и удачи в новых релизах! :)