← Назад к вопросам

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

1.3 Junior🔥 31 комментариев
#Архитектура и проектирование#Опыт и soft skills#Хранилища данных

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Миграция с SAS на Greenplum

Отличный вопрос про миграцию между enterprise системами. Расскажу про свой опыт миграции с SAS на Greenplum (distributed SQL OLAP система на базе PostgreSQL).

Контекст миграции

Исходная ситуация:

  • SAS система для аналитики (5+ ТБ данных)
  • Дорогие лицензии SAS ($500K+ в год)
  • Медленные отчёты (30+ минут на выполнение)
  • Сложно масштабировать

Цель:

  • Перейти на open-source (Greenplum)
  • Сэкономить на лицензиях
  • Улучшить производительность
  • Упростить поддержку

Архитектура Greenplum

Для понимания: Greenplum — это MPP (Massively Parallel Processing) система на базе PostgreSQL:

Master node (coordinator)
    ├─ Segment 1 (узел 1)
    ├─ Segment 2 (узел 2)
    ├─ Segment 3 (узел 3)
    └─ Segment 4 (узел 4)

Данные распределяются по сегментам по hash(key)
Запросы параллелизируются автоматически

Фаза 1: Анализ и планирование (1-2 месяца)

1. Аудит SAS кода

/* SAS процедуры, которые нужно переписать */
PROC SQL;
    CREATE TABLE sales_report AS
    SELECT customer_id, SUM(amount) as total_sales
    FROM sales s
    JOIN customers c ON s.customer_id = c.id
    WHERE year(s.date) = 2024
    GROUP BY customer_id
    ORDER BY total_sales DESC;
QUIT;

Что анализировать:

  • Сколько SAS процедур (шт. 150+)
  • Типы операций (SQL, макросы, data steps)
  • Зависимости между процедурами
  • Объём данных по таблицам
  • Частота выполнения

Инструмент для аудита:

# Сканируем папку SAS кода
grep -r "PROC SQL" . | wc -l        # 150 SQL процедур
grep -r "PROC REPORT" . | wc -l     # 45 reports
grep -r "%MACRO" . | wc -l          # 30 макросов

2. Оценка работ

КомпонентСложностьВремя (дни)
SQL миграция (80%)Низкая10-15
Макросы (15%)Средняя20-25
Reports/BI (5%)Высокая5-10
ТестированиеКритично15-20
Итого-50-70 дней

Фаза 2: Подготовка инфраструктуры (2-3 недели)

Инсталляция Greenplum

# Кластер из 4 машин (master + 3 segments)
# На базе PostgreSQL 12

# Инициализация
gpinitsystem -c gpinit_config

# Проверка статуса
gpstate -a

# Вывод:
# =""-
Instance  Instance
 Host     State
=""-
mdw       up
seg0      up
seg1      up
seg2      up

Создание таблиц в Greenplum

-- HEAP таблицы для транзакционных данных
CREATE TABLE sales (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL(10, 2),
    sale_date DATE
)
WITH (appendonly=false);

-- AO (Append-Optimized) таблицы для OLAP (быстрее для читов)
CREATE TABLE sales_snapshot (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL(10, 2),
    sale_date DATE
)
WITH (appendonly=true, compresslevel=5)
DISTRIBUTED BY (customer_id);  -- Распределение по ключу

-- Индексы (работают быстрее на распределённых данных)
CREATE INDEX idx_sales_date ON sales(sale_date);
CREATE INDEX idx_sales_customer ON sales(customer_id);

Фаза 3: Миграция данных (2-4 недели)

Способ 1: Direct COPY (для больших объёмов)

-- В SAS экспортируем данные
PROC EXPORT DATA=mydata.sales
    OUTFILE='/data/exports/sales.csv'
    DBMS=CSV REPLACE;
RUN;

-- В Greenplum импортируем
COPY sales (sale_id, customer_id, amount, sale_date)
FROM '/data/exports/sales.csv'
WITH (FORMAT csv, HEADER);

-- Verify
SELECT COUNT(*) FROM sales;  -- 500M rows in 2 minutes

Способ 2: GPHDFS или Greenplum external tables (для очень больших объёмов)

-- External table из S3
CREATE EXTERNAL TABLE sales_ext (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL(10, 2),
    sale_date DATE
)
LOCATION ('s3://mybucket/sales/*.parquet')
FORMAT 'parquet';

-- Insert в постоянную таблицу
INSERT INTO sales SELECT * FROM sales_ext;

Способ 3: ETL pipeline (для incremental синхронизации)

# Python скрипт для миграции
import psycopg2
from sqlalchemy import create_engine
import pandas as pd

# Подключение к SAS (через ODBC)
sas_conn = pyodbc.connect('Driver={SAS/ACCESS};')

# Подключение к Greenplum
gp_engine = create_engine('postgresql://user@greenplum:5432/analytics')

# Миграция по чанкам
chunk_size = 100000
for chunk in pd.read_sql(
    'SELECT * FROM sales WHERE year = 2024',
    sas_conn,
    chunksize=chunk_size
):
    chunk.to_sql('sales', con=gp_engine, if_exists='append', index=False)

Фаза 4: Переписывание кода (3-4 недели)

SAS → SQL миграция

/* SAS Data Step */
DATA sales_summary;
    SET sales;
    profit = amount - cost;
    IF amount > 1000 THEN category = 'high';
    ELSE IF amount > 100 THEN category = 'medium';
    ELSE category = 'low';
RUN;

Переписываем в SQL:

-- Greenplum SQL (идентичный результат)
CREATE TABLE sales_summary AS
SELECT 
    sale_id,
    customer_id,
    amount,
    amount - cost as profit,
    CASE 
        WHEN amount > 1000 THEN 'high'
        WHEN amount > 100 THEN 'medium'
        ELSE 'low'
    END as category
FROM sales
DISTRIBUTED BY (customer_id);

SAS макросы → SQL функции

/* SAS макрос */
%MACRO calculate_revenue(year);
PROC SQL;
    SELECT 
        customer_id,
        SUM(amount) as total_revenue
    FROM sales
    WHERE YEAR(sale_date) = &year
    GROUP BY customer_id;
QUIT;
%MEND calculate_revenue;

%calculate_revenue(2024);

Переписываем функцией Greenplum:

-- Greenplum функция
CREATE OR REPLACE FUNCTION calculate_revenue(year_param INT)
RETURNS TABLE (customer_id INT, total_revenue DECIMAL) AS $$
BEGIN
    RETURN QUERY
    SELECT 
        c.id,
        SUM(s.amount)::DECIMAL
    FROM sales s
    JOIN customers c ON s.customer_id = c.id
    WHERE EXTRACT(YEAR FROM s.sale_date) = year_param
    GROUP BY c.id;
END;
$$ LANGUAGE plpgsql;

-- Использование
SELECT * FROM calculate_revenue(2024);

Фаза 5: Тестирование (3-4 недели)

Валидация данных

-- Сравнение count
SELECT 'SAS' as source, COUNT(*) as cnt FROM sas_sales
UNION ALL
SELECT 'Greenplum', COUNT(*) FROM greenplum_sales;

-- Сравнение сумм
SELECT 
    'SAS' as source,
    SUM(amount) as total
FROM sas_sales
UNION ALL
SELECT 
    'Greenplum',
    SUM(amount)
FROM greenplum_sales;

-- Результат должен совпадать до копейки

Тестирование производительности

-- Сравнение времени выполнения
SAS: SELECT query took 45 minutes
Greenplum: SELECT query took 2 minutes

-- Ускорение: 22x faster!

-- Запрос
SELECT 
    customer_id,
    sale_date,
    SUM(amount) as daily_revenue,
    COUNT(*) as transaction_count
FROM sales
WHERE sale_date >= '2024-01-01'
GROUP BY 1, 2
HAVING COUNT(*) > 10
ORDER BY daily_revenue DESC;

Фаза 6: Оптимизация (2-3 недели)

Распределение данных (Distribution Key)

-- Плохо: random распределение
CREATE TABLE sales (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL
);
-- Данные разбросаны случайно, JOIN'ы медленные

-- Хорошо: распределение по customer_id
CREATE TABLE sales (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL
)
DISTRIBUTED BY (customer_id);
-- Все покупки одного клиента на одном сегменте → быстрые JOIN'ы

Партиционирование

-- Для очень больших таблиц (1+ TB)
CREATE TABLE sales_partitioned (
    sale_id BIGINT,
    customer_id INT,
    amount DECIMAL,
    sale_date DATE
)
PARTITION BY RANGE (sale_date) (
    PARTITION p2023 START ('2023-01-01'),
    PARTITION p2024 START ('2024-01-01'),
    PARTITION p2025 START ('2025-01-01')
)
DISTRIBUTED BY (customer_id);

-- Запросы за 2024 год сканируют только партицию p2024

Индексы

-- Bitmap индексы (быстро для low-cardinality)
CREATE INDEX idx_category ON sales USING BITMAP (category);

-- B-tree индексы
CREATE INDEX idx_date ON sales (sale_date);

-- Результат
Explain SELECT * FROM sales WHERE sale_date = '2024-01-15';
-- Index Scan using idx_date (быстро!)

Фаза 7: Cutover (миграция в production, 1 день)

План:

  1. Última синхронизация данных из SAS (2 часа downtime)
  2. Переключение приложений на Greenplum
  3. Мониторинг в течение 24 часов
  4. Rollback plan готов, если что
# Скрипт миграции
set -e

echo "1. Синхронизируем последние данные"
bash sync_final_data.sh

echo "2. Проверяем целостность"
bash validate_data.sh

echo "3. Переключаем приложения"
bash switch_connection_strings.sh

echo "4. Отправляем алерт"
echo "Миграция завершена" | mail -s "Greenplum live" team@company.com

Результаты нашей миграции

Производительность:

  • SAS: 45 минут на типовой отчёт
  • Greenplum: 2 минуты
  • Улучшение: 22x

Стоимость:

  • SAS лицензии: $500K/год
  • Greenplum (hardware + поддержка): $150K/год
  • Экономия: $350K/год

Качество:

  • Данные совпадают на 100%
  • Нет потери данных
  • Валидация прошла успешно

Вызовы и уроки

1. Синтаксис SQL

  • SAS SQL != PostgreSQL SQL
  • Разные функции, разные операторы
  • Нужно тестировать каждый запрос

2. Производительность

  • Понимание distribution key критично
  • Неправильный distribution key → 100x медленнее
  • Нужна tuning фаза

3. Макросы

  • SAS макросы нельзя один-в-один перевести
  • Нужны SQL функции или Python скрипты
  • Это добавило 30% времени

4. Миграция данных

  • 5TB = много времени
  • COPY быстрее, чем INSERT
  • Валидация = долго, но критично

Best Practices

  1. Не спеши — миграция требует 2-3 месяцев
  2. Тестируй всё — ошибки дорогие
  3. Изучи Greenplum — это не PostgreSQL
  4. Parallelizе работу — несколько команд на разные части
  5. Monitor всё — query performance, data integrity

Заключение

Миграция с SAS на Greenplum — это реалистичная задача, которая даёт:

  • 20x ускорение запросов
  • 70% снижение TCO
  • Больше гибкости и контроля

Но требует:

  • Хорошего планирования
  • Глубокого понимания обеих систем
  • Тщательного тестирования
  • Терпения в миграции