Наверное, всем знакома ситуация, когда собираешь красивый отчет в BI-системе, пользуешься им полгода, а потом он с каждым днем тормозит все сильнее? Графики обновляются дольше обычного, таблицы медленно грузятся. А все потому, что BI работает на одной базе на пределе своих возможностей из-за перегруженности.
Пример классической схемы, с которой начинают почти все компании: BI + база данных. Пока стартапы не могут позволить себе дорогостоящее озеро данных или DWH, они устанавливают базу данных, заполняют ее и прикручивают рядом BI-систему. Вот только данные в растущей компании имеют свойство прибывать. К примеру, у стартапа в первые месяцы работы было пять заказов в день, затем 100, 1 000 и еще больше. День за днем, год за годом все данные складываются в одну базу данных, и в итоге получается тяжелая, медленная система, которой банально неудобно пользоваться. Можно увеличить мощность базы данных, но это лишь отложит проблему на время, а не решит ее. А можно снять нагрузку с базы и доверить обработку накопленных данных другим инструментам и системам хранения.
В этой статье я расскажу, как решить проблему с базой данных, которая трещит по швам, с помощью облачных managed-сервисов.

Всем привет! Меня зовут Алексей Лицов, я представляю команду платформы данных в Cloud.ru. К нам, как облачному провайдеру, приходят и стартапы, и крупные сегменты бизнеса с похожей проблемой: как сохранить все накопленные за годы данные, не сломать уже работающую в проде систему и при этом получить быстрые, надежные решения с аналитикой, дашбордами и всем остальным.
Приведу пример. Компания — поставщик автотоваров столкнулась с проблемой ухудшения производительности базы данных. Все заказы с начала существования компании складывались в единую базу данных PostgreSQL на одном сервере (и онлайн-заказы, и история, и данные для аналитики), что привело к долгому выполнению запросов и тормозам на BI. Проблему можно решить несколькими способами, давайте их рассмотрим.
Проблема: одна база на все задачи
Мы видим типичную картину растущего бизнеса: таблицы с несколькими миллионами данных, которые пополняются каждую минуту, каждый день, год за годом. Исторические данные уже нельзя выбросить: на них завязаны отчеты и анализ трендов.
Пока нагрузки были ниже, такая схема держалась за счет запаса по ресурсам. BI строит дашборды по всем данным из таблиц за всю накопленную историю и превращает каждый запрос в проход по гигантским таблицам. Аналитики запускают тяжелые агрегирующие запросы, которые конкурируют за ресурсы базы. При этом отказаться от базы нельзя: она зашита в интеграции, отчетность и внутренние процессы.

Можно нарастить ресурсы, но через какое-то время база снова упрется в ограничения и проблема вернется. Нужно придумать что-то такое, что избавит от причины проблемы, а не от следствий.
Идея: разделить не систему, а время
В такой ситуации достаточно стандартное решение — это разделить ответственность за обработку и хранение исторических и real-time данных. Должна быть возможность и наблюдать за текущими продажами, например, за последние пару дней, и анализировать данные за прошедшие годы.
Чтобы разделить систему на два слоя, нам нужно ответить на вопросы:
Где мы будем хранить исторические (холодные) и real-time (горячие) данные?
Какими инструментами мы будем их обрабатывать?
Мы точно знаем, что горячие данные — это оперативные данные, доступ к которым нужен «вотпрямщас», обращаются к ним намного чаще, чем к холодным. Значит, их можно оставить там, где они и были, — в базе данных. При этом мы точно знаем, что система на маленьких объемах, использующая базу данных, налажена и проверена временем.
А вот исторические данные — это петабайты информации, которые нет смысла хранить и тем более обрабатывать на стороне базы. Логичнее переложить всю историю в дешевое хранилище, например, в S3, и дальше все данные старше, допустим, трех дней отправлять туда же (а каким инструментом, вы узнаете дальше).
Схематично это будет выглядеть следующим образом:

Архитектура: из каких «кирпичей» собрали решение
Когда идея с разделением данных по времени стала понятной, дальше нужно превратить ее в рабочее решение, которое не поломает бизнес-процессы.
Мы предоставили клиенту выбор: собрать решение с нуля на платформе Kubernetes или использовать управляемые PaaS-сервисы по обработке данных, которые уже интегрированы между собой. Формально первый вариант кажется дешевле, но на практике требует отдельной DevOps-команды, которая будет поддерживать каждый сервис, следить за обновлениями, мониторингом, логированием и бэкапами. Второй вариант снимает большую часть этой рутины и позволяет сосредоточиться на самой задаче с данными, а не на интеграциях и разворачивании отдельных продуктов.
Победил второй вариант, нам предстояло собрать решение на облачной платформе данных с готовым набором managed-сервисов — Evolution Data Platform. Это полноценное платформенное решение с общим интерфейсом и сервисами управления.
Для этого кейса платформа подходила лучше всего, потому что там уже есть все нужные для архитектуры управляемые сервисы: Spark, Metastore, Trino, Airflow и BI. Нам было достаточно создать инстансы этих продуктов и продумать бизнес-логику разделения данных во времени.
Перейду к решению нашей первой задачи — переложить холодные данные в объектное хранилище, чтобы освободить переполненную базу.
Как перенести холодные данные в хранилище S3
Излюбленный инструмент всех дата-инженеров — это Spark, а в случае с облаком — Managed Spark.

Давайте продумаем план действий.
Написать pySpark-скрипт не составит труда ни одному дата-инженеру, я же сосредоточусь на алгоритме:
Инициализировать Spark-сессию.
Выбрать источник данных, в нашем случае это jdbc-подключение к базе данных.
Выбрать таблицы, которые хотим переместить в S3.
Указать условие, что из таблиц надо забирать данные старше трех дней.
Указать путь в S3 — это конечный пункт доставки данных.
Удалить из базы данные старше трех дней.
Схематично это выглядит так:

В S3 это будет выглядеть следующим образом:

Один запуск Spark-скрипта – один датасет – один parquet-файл.
Если построить график числа записей в таблице, то на временной шкале получится следующая картина:

График в виде пилы говорит о том, что каждый запуск Spark-скрипта вырезает данные из БД и складывает их в S3. В боевом сценарии, конечно же, в ноль значения опускаться не будут, ведь удаляются только данные за последние три дня.
Один из важных моментов — добавить в Spark-скрипт логику агрегации, чтобы входные петабайты данных превратить на выходе в килобайты и уже их визуализировать в Managed BI. Код агрегации будет для каждой бизнес-задачи свой, в моем примере это джойн товаров и заказов и сумма продаж:

Как визуализировать в BI-системе данные из S3
BI-система работает с реляционными таблицами, и раньше мы визуализировали данные напрямую из базы. Теперь горячие данные остались в таблицах базы данных, а холодные лежат в виде parquet-файлов в S3.
Уместным будет вопрос «А как написать SQL-запрос к parquet-файлу в S3?».
Чтобы система не сломалась и клиент так же мог работать с привычными инструментами, воспользуемся сервисом для хранения метаданных таблиц — Managed Metastore. Он позволяет регистрировать сущность external-таблиц и обращаться к ним, как если бы это была обычная таблица в базе данных.

Вернусь к примеру. Чтобы читать данные из S3 через SQL, надо выполнить элементарный запрос:

А так выглядит результат запроса:

Задача с чтением холодных данных решена. Напомню важное условие: BI-система должна получать и исторические, и свежие данные для анализа, поэтому нужен способ забирать их в рамках одного подключения.
Объединяем горячие и холодные данные
В этом поможет Managed Trino — массивно-параллельный аналитический SQL-движок с возможностью обрабатывать данные из разных источников. Он умеет вытягивать данные через Managed Metastore(в нашем случае холодные данные) и через обычный PostgreSQL-коннектор (горячие данные) в рамках одного jdbc-подключения.

Для этого в инстансе Managed Trino нужно выбрать соответствующие коннекторы:

Пример запроса, на основании которого строится график продаж по заказчикам:

Запрос получается очень легковесный, потому что объединяет горячие данные и агрегаты холодных данных. А вот самые тяжеловесные операции по агрегации холодных данных проходят ночью раз в день на стороне Managed Spark + S3, а не на стороне базы данных.

Итоговая архитектура системы выглядит так:

Резюмирую, что мы уже сделали. С помощью Managed Spark перекладываем данные в S3, готовим агрегаты и складываем туда же. Для чтения из S3 через SQL-запросы используем Managed Metastore. А благодаря Managed Trino можем за один присест тянуть агрегированные холодные данные из S3 и горячие из PostgreSQL.
Последний штрих: запуск Spark-скрипта по расписанию
Почти все готово, вот только не очень удобно каждый раз запускать Spark-job вручную. Добавим автоматизацию с помощью Managed Airflow — сервис для оркестрации задач, который позволяет планировать сложную бизнес-логику, объединять задачи в пайплайны и запускать их по расписанию.

Воспользуюсь Public API как самым простым способом вызова задачи. Схематично это выглядит так:

Чтобы все заработало, напишем DAG-файл, настроим в нем расписание и положим в S3-бакет. Наш Managed Airflow сразу же его подхватит и покажет на своем UI.
Готово, теперь Spark-задачи запускаются по расписанию и система работает автономно.

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

Что получил клиент на техническом уровне:
Намного больше гибкости в системе, почти безграничные возможности делать самые «тяжелые» отчеты без потери в производительности.
Надежную систему, которая построена полностью на облачных Managed-сервисах. Все компоненты работают в управляемой Kubernetes-инфраструктуре с возможностью автоскейлинга.
Возможность передать рутинные задачи типа обновлений, бэкапов и логирования платформе.
И конечно, это дало бизнес-результаты:
Хранение данных стало дешевле. Исторические (холодные) данные лежат в более дешевом S3-хранилище. А дорогостоящий PostgreSQL теперь хранит только актуальный срез заказов (горячие данные).
Аналитики клиента увидели качественный скачок: дашборды в Managed BI перестали «думать» десятки секунд, сложные запросы можно запускать без страха задушить продакшен. При этом вся история по продажам остается под рукой: можно посмотреть холодные данные в оригинальном виде или на их агрегаты.
Система теперь автоматизирована, полный цикл ETL-процессов работает без ручного вмешательства.
Текущая архитектура позволяет бизнесу не задумываться о производительности системы даже при кратном увеличении данных.
