Расскажи про миграцию с SAS на Greenplum

Ответ

Я участвовал в проекте по миграции аналитической платформы с проприетарного стека SAS на открытую MPP-СУБД Greenplum. Основная цель была — снизить стоимость лицензий и повысить производительность обработки больших объемов данных.

План и этапы миграции:

  1. Инвентаризация и анализ (самый важный этап):

    • Составили каталог всех SAS-скриптов (.sas), макросов, jobs из SAS DI Studio и расписаний.
    • Классифицировали их по типу: извлечение данных (ETL), трансформация, построение отчетов (PROC REPORT), статистический анализ (PROC MEANS, PROC FREQ).
    • Определили, какую логику можно напрямую переложить на SQL Greenplum, а что требует переписывания на Python (например, сложные макросы).
  2. Перенос и преобразование данных:

    • Данные из SAS-библиотек (SAS7BDAT) выгружали с помощью утилиты PROC EXPORT в промежуточный формат Parquet на HDFS, что сохраняло типы данных и сжимало объем.
    • В Greenplum создавали схему, таблицы с оптимальным распределением (DISTRIBUTED BY). Ключ распределения выбирали на основе джойнов в факт-таблицах.
    • Загрузка выполнялась через gpfdist для максимальной скорости:
      
      -- Создание внешней таблицы, указывающей на файлы в HDFS
      CREATE EXTERNAL TABLE ext_sales (id int, date date, amount decimal(10,2))
      LOCATION ('gpfdist://hdfs-namenode:8081/data/sales/*.parquet')
      FORMAT 'parquet';

    -- Загрузка во внутреннюю таблицу INSERT INTO internal_sales SELECT * FROM ext_sales;

  3. Переписывание бизнес-логики:

    • Простой SQL: Процедуры PROC SQL переводились почти один-в-один в стандартный ANSI SQL для Greenplum.
    • Сложные трансформации: Логику из DATA STEP с множественными условиями и циклами переписывали на Python UDF (User-Defined Functions), которые выполнялись внутри Greenplum, или выносили в отдельные PySpark-джобы, если трансформация была очень ресурсоемкой.
    • Статистика и агрегация: Заменяли PROC MEANS, PROC SUMMARY на агрегатные функции SQL (AVG, SUM, PERCENTILE_CONT) и оконные функции для расчетов.
      -- Пример: Замена PROC MEANS + BY GROUP
      -- SAS: PROC MEANS DATA=sales MEAN SUM MAXDEC=2; CLASS region; VAR revenue; RUN;
      -- Greenplum:
      SELECT 
      region,
      ROUND(AVG(revenue), 2) as avg_revenue,
      SUM(revenue) as total_revenue
      FROM sales
      GROUP BY region;
  4. Оптимизация под MPP-архитектуру:

    • Партиционирование: Крупные таблицы партиционировали по дате (PARTITION BY RANGE (date_column)).
    • Индексы: В Greenplum (колоночном хранилище) индексы используются реже, чем в SAS. Вместо этого мы настраивали сегментирование (distribution) и сжатие (WITH (APPENDONLY=true, COMPRESSTYPE=zlib)).
    • Анализ запросов: Использовали EXPLAIN ANALYZE для поиска узких мест — часто это были cross-segment операции (перераспределение данных Redistribute Motion), которые мы устраняли, меняя ключ дистрибуции.

Основные сложности:

  • Отсутствие прямых аналогов некоторых SAS-процедур для нишевого статистического анализа. Для этого пришлось интегрировать Greenplum с R или Python (scikit-learn, statsmodels) через PL/R или PL/Python.
  • Миграция сложных SAS-макросов, которые генерировали динамический код. Их переписывали на Python, используя шаблонизаторы (Jinja2) для генерации SQL.
  • Обеспечение сопоставимой производительности отчетов. В Greenplum мы добились этого за счет предварительной агрегации данных в OLAP-кубы или использование материализованных представлений.

Ответ 18+ 🔞

Ну вот, сидишь ты такой на работе, а тебе говорят: «А давай-ка, дружок, переедем с этого старого доброго SAS на какую-нибудь открытую штуку, чтобы лицензии не платить овердохуища денег». И выбор пал на Greenplum. Ну, Greenplum так Greenplum, хуй с горы, главное — отвязаться от этих вечных счетов.

План, или как мы эту кашу расхлёбывали:

  1. Разбор полётов, он же инвентаризация (этап, где все охуевают от объёма работы).

    • Начинаем собирать всё, что нажито непосильным трудом: скрипты .sas, макросы, джобы из SAS DI, расписания — короче, всё, что шевелится и пахнет данными.
    • Дальше сортируем этот зоопарк: вот это — выгрузка-загрузка (ETL), вот это — какие-то преобразования, а вот это — отчёты, которые бухгалтерия требует раз в квартал, и статистика, от которой у нормального человека голова болит.
    • Самое весёлое — понять, что из этого можно тупо переписать на SQL, а что — такое замысловатое, что проще выкинуть и написать на Python с нуля. Чувствуешь подвох? А он уже тут как тут.
  2. Тащить вагоны, то есть перенос данных.

    • Данные из SAS-библиотек выковыривали через PROC EXPORT и пихали в Parquet на HDFS. Формат норм, сжимает хорошо, типы не теряет — красота.
    • В Greenplum создаём таблицы и сразу думаем, как их правильно распределить по сегментам (DISTRIBUTED BY). Ключ распределения выбирали не абы как, а смотря, по каким полям чаще всего джойнятся. Ошибёшься тут — потом запрос будет полчаса жрать ресурсы, как не в себя.
    • Загружали через gpfdist, чтобы быстрее, блядь, было. Выглядело это примерно так:
      
      -- Вешаем внешнюю таблицу прямо на файлы в HDFS
      CREATE EXTERNAL TABLE ext_sales (id int, date date, amount decimal(10,2))
      LOCATION ('gpfdist://hdfs-namenode:8081/data/sales/*.parquet')
      FORMAT 'parquet';

    -- И хуяк — заливаем внутрь INSERT INTO internal_sales SELECT * FROM ext_sales;

  3. Самая творческая часть — переписывание логики.

    • Простой SQL: Если в SAS был PROC SQL — то тут вообще сказка, переписывается один-в-один, только синтаксис чуть подправить. Удивление пиздец, как легко.
    • Сложная хуйня: А вот эти ваши DATA STEP с кучей условий и циклов — это, ядрёна вошь, отдельная история. Их либо в Python UDF заворачивали, чтобы выполнялись прямо внутри Greenplum, либо, если совсем мясо, выносили в отдельные PySpark-джобы.
    • Статистика и агрегация: Всякие PROC MEANS и PROC SUMMARY заменяли на родные агрегатные функции SQL и оконные функции. Получалось даже изящнее.
      -- Раньше в SAS была простыня кода, а теперь:
      SELECT 
      region,
      ROUND(AVG(revenue), 2) as avg_revenue,
      SUM(revenue) as total_revenue
      FROM sales
      GROUP BY region;
      -- Красота, ёпта.
  4. Тонкая настройка, или как заставить Greenplum летать.

    • Партиционирование: Большие таблицы резали по дате (PARTITION BY RANGE), чтобы запросы к актуальным данным летали.
    • Индексы: А вот тут сюрприз — в колоночном Greenplum индексы часто не нужны, как в том анекдоте про дырку от бублика. Вместо них игрались сегментированием и сжатием данных.
    • Анализ запросов: Рубились в EXPLAIN ANALYZE как в косынку. Видишь в плане Redistribute Motion — значит, данные болтаются туда-сюда между сегментами, тормозят всё. Лечилось сменой ключа дистрибуции. Волнение ебать, когда находишь такую проблему и фиксишь её.

А теперь, блядь, про сложности (куда же без них):

  • Некоторые SAS-процедуры для узкоспециального стата оказались уникальными в своём роде. Пришлось городить огород и интегрировать Greenplum с R или Python, чтобы те же расчёты делать. Чёрта в душу, сколько возни.
  • Сложные SAS-макросы, которые генерируют код на лету, — это просто пиздец, извините за выражение. Их разбирали по косточкам и переписывали на Python, используя шаблонизаторы вроде Jinja2.
  • И главное — чтобы отчёты строились не медленнее, чем раньше. Добивались этого кто во что горазд: кто предрасчитанными агрегатами, кто материализованными представлениями. Лишь бы пользователи не орали «где мой отчёт, я его вчера заказывал!».