refactor(sql): заменен тип сжатия zlib на zstd для AO-таблиц

- Зачем:
  - zstd (level 1) является современным стандартом для Greenplum 6.0+, обеспечивая более высокую скорость декомпрессии и лучшее сжатие.
- Что:
  - обновлены все DDL стейджинга (STG) и базовых таблиц.
  - обновлена архитектурная документация (ADR-3) и планы реализации.
  - исправлены примеры кода в Airflow DAG и описании ETL.
- Проверка:
  - успешное выполнение CREATE TABLE с новыми параметрами в Greenplum 6.27.1.
This commit is contained in:
2026-03-01 20:26:56 +03:00
parent 92f3828a45
commit 4d793a9c11
14 changed files with 23 additions and 23 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ def _create_table() -> None:
customer_id BIGINT NOT NULL, customer_id BIGINT NOT NULL,
amount NUMERIC(12,2) NOT NULL amount NUMERIC(12,2) NOT NULL
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
DISTRIBUTED BY (order_id); DISTRIBUTED BY (order_id);
""" """
with get_gp_conn() as conn, conn.cursor() as cur: with get_gp_conn() as conn, conn.cursor() as cur:
+2 -2
View File
@@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS stg.tickets (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), -- время загрузки в Greenplum load_dttm TIMESTAMP NOT NULL DEFAULT now(), -- время загрузки в Greenplum
batch_id TEXT -- идентификатор батча (из Airflow run_id) batch_id TEXT -- идентификатор батча (из Airflow run_id)
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
DISTRIBUTED BY (book_ref); -- распределение по ключу связи с bookings DISTRIBUTED BY (book_ref); -- распределение по ключу связи с bookings
``` ```
@@ -123,7 +123,7 @@ CREATE TABLE IF NOT EXISTS stg.tickets (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Распределяем по book_ref, чтобы джойны tickets → bookings по book_ref были без motion. -- Распределяем по book_ref, чтобы джойны tickets → bookings по book_ref были без motion.
DISTRIBUTED BY (book_ref); DISTRIBUTED BY (book_ref);
+9 -9
View File
@@ -101,10 +101,10 @@ GP-специфичная best practice, которую забывают даж
- 18 из 28 таблиц имеют неявный heap (нет `WITH`) — студент не видит, что выбор сделан - 18 из 28 таблиц имеют неявный heap (нет `WITH`) — студент не видит, что выбор сделан
- **Целевая раскладка по storage:** - **Целевая раскладка по storage:**
- **AO Column Store**: `dds.dim_calendar` (write-once, generate_series) - **AO Column Store**: `dds.dim_calendar` (write-once, generate_series)
- **AO Row + zlib**: ODS snapshot-справочники (`airports`, `airplanes`, `routes`, `seats`) - **AO Row + zstd**: ODS snapshot-справочники (`airports`, `airplanes`, `routes`, `seats`)
— перевести загрузку с UPSERT на TRUNCATE+INSERT (честнее для full snapshot семантики) — перевести загрузку с UPSERT на TRUNCATE+INSERT (честнее для full snapshot семантики)
- **AO Row + zlib**: `dds.dim_tariffs` (только INSERT, нет UPDATE) - **AO Row + zstd**: `dds.dim_tariffs` (только INSERT, нет UPDATE)
- **AO Row + zlib**: `dm.route_performance` (full rebuild, по дизайну) - **AO Row + zstd**: `dm.route_performance` (full rebuild, по дизайну)
- **Heap (явный)**: ODS транзакционные (`bookings`, `tickets`, `flights`, `segments`, - **Heap (явный)**: ODS транзакционные (`bookings`, `tickets`, `flights`, `segments`,
`boarding_passes`) — row-level UPDATE при SCD1 UPSERT `boarding_passes`) — row-level UPDATE при SCD1 UPSERT
- **Heap (явный)**: DDS измерения с UPDATE (`dim_airports`, `dim_airplanes`, - **Heap (явный)**: DDS измерения с UPDATE (`dim_airports`, `dim_airplanes`,
@@ -228,7 +228,7 @@ GP-специфичная best practice, которую забывают даж
```sql ```sql
-- 1. Собрать новую партицию во временную таблицу -- 1. Собрать новую партицию во временную таблицу
CREATE TABLE tmp_fact_20170102 (LIKE dds.fact_flight_sales) CREATE TABLE tmp_fact_20170102 (LIKE dds.fact_flight_sales)
WITH (appendonly=true, orientation=column, compresstype=zlib); WITH (appendonly=true, orientation=column, compresstype=zstd);
INSERT INTO tmp_fact_20170102 SELECT ... FROM ods... WHERE flight_date = '2017-01-02'; INSERT INTO tmp_fact_20170102 SELECT ... FROM ods... WHERE flight_date = '2017-01-02';
-- 2. Атомарно заменить партицию (без DELETE, без UPDATE) -- 2. Атомарно заменить партицию (без DELETE, без UPDATE)
@@ -258,11 +258,11 @@ DROP TABLE tmp_fact_20170102;
| Storage | Таблицы | Почему | | Storage | Таблицы | Почему |
|---------|---------|--------| |---------|---------|--------|
| **AO Column** zlib | `dds.dim_calendar` | Write-once (generate_series), никогда не обновляется. Колоночное хранение идеально для аналитических скан. | | **AO Column** zstd | `dds.dim_calendar` | Write-once (generate_series), никогда не обновляется. Колоночное хранение идеально для аналитических скан. |
| **AO Column** zlib | `dm.route_performance` | Full rebuild (TRUNCATE+INSERT), чисто аналитические чтения. | | **AO Column** zstd | `dm.route_performance` | Full rebuild (TRUNCATE+INSERT), чисто аналитические чтения. |
| **AO Row** zlib | STG: все 9 таблиц | Уже реализовано. Append-only, иммутабельные батчи. | | **AO Row** zstd | STG: все 9 таблиц | Уже реализовано. Append-only, иммутабельные батчи. Примечание: используем **zstd (level 1)** вместо zlib, так как он обеспечивает более высокую скорость декомпрессии и лучшее сжатие в современных GP-кластерах (6.0+). |
| **AO Row** zlib | ODS snapshot: `airports`, `airplanes`, `routes`, `seats` | Полный snapshot каждый раз. Перевести загрузку с UPSERT на TRUNCATE+INSERT — честнее для семантики «текущий срез». | | **AO Row** zstd | ODS snapshot: `airports`, `airplanes`, `routes`, `seats` | Полный snapshot каждый раз. Перевести загрузку с UPSERT на TRUNCATE+INSERT — честнее для семантики «текущий срез». |
| **AO Row** zlib | `dds.dim_tariffs` | Только INSERT новых тарифов, UPDATE не используется. | | **AO Row** zstd | `dds.dim_tariffs` | Только INSERT новых тарифов, UPDATE не используется. |
| **Heap** (явный) | ODS транзакционные: `bookings`, `tickets`, `flights`, `segments`, `boarding_passes` | Row-level UPDATE при SCD1 UPSERT. Heap обязателен. | | **Heap** (явный) | ODS транзакционные: `bookings`, `tickets`, `flights`, `segments`, `boarding_passes` | Row-level UPDATE при SCD1 UPSERT. Heap обязателен. |
| **Heap** (явный) | DDS измерения с UPDATE: `dim_airports`, `dim_airplanes`, `dim_passengers`, `dim_routes` | SCD1/SCD2 UPSERT с row-level UPDATE. | | **Heap** (явный) | DDS измерения с UPDATE: `dim_airports`, `dim_airplanes`, `dim_passengers`, `dim_routes` | SCD1/SCD2 UPSERT с row-level UPDATE. |
| **Heap** (явный) | `dds.fact_flight_sales` | UPDATE (is_boarded, seat_no меняются). | | **Heap** (явный) | `dds.fact_flight_sales` | UPDATE (is_boarded, seat_no меняются). |
+1 -1
View File
@@ -74,7 +74,7 @@ CREATE TABLE IF NOT EXISTS stg.{table} (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
DISTRIBUTED BY ({distribution_key}); DISTRIBUTED BY ({distribution_key});
``` ```
+1 -1
View File
@@ -7,5 +7,5 @@ CREATE TABLE IF NOT EXISTS public.orders (
customer_id BIGINT NOT NULL, customer_id BIGINT NOT NULL,
amount NUMERIC(12,2) NOT NULL amount NUMERIC(12,2) NOT NULL
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
DISTRIBUTED BY (order_id); DISTRIBUTED BY (order_id);
+1 -1
View File
@@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS stg.airplanes (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: airplane_code -- Ключ распределения: airplane_code
-- Обоснование: airplane_code — это уникальный идентификатор самолёта. -- Обоснование: airplane_code — это уникальный идентификатор самолёта.
-- Использование airplane_code обеспечивает: -- Использование airplane_code обеспечивает:
+1 -1
View File
@@ -31,7 +31,7 @@ CREATE TABLE IF NOT EXISTS stg.airports (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: airport_code -- Ключ распределения: airport_code
-- Обоснование: airport_code — это уникальный идентификатор аэропорта. -- Обоснование: airport_code — это уникальный идентификатор аэропорта.
-- Использование airport_code обеспечивает: -- Использование airport_code обеспечивает:
+1 -1
View File
@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS stg.boarding_passes (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: ticket_no -- Ключ распределения: ticket_no
-- Обоснование: ticket_no — это основной бизнес-ключ для билетов. -- Обоснование: ticket_no — это основной бизнес-ключ для билетов.
-- Использование ticket_no обеспечивает: -- Использование ticket_no обеспечивает:
+1 -1
View File
@@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS stg.bookings (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT NOT NULL batch_id TEXT NOT NULL
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: book_ref -- Ключ распределения: book_ref
-- Обоснование: book_ref — это уникальный идентификатор бронирования. -- Обоснование: book_ref — это уникальный идентификатор бронирования.
-- Использование book_ref обеспечивает: -- Использование book_ref обеспечивает:
+1 -1
View File
@@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS stg.flights (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: flight_id -- Ключ распределения: flight_id
-- Обоснование: flight_id — это уникальный идентификатор рейса. -- Обоснование: flight_id — это уникальный идентификатор рейса.
-- Использование flight_id обеспечивает: -- Использование flight_id обеспечивает:
+1 -1
View File
@@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS stg.routes (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: route_no -- Ключ распределения: route_no
-- Обоснование: route_no — бизнес-идентификатор маршрута и часто используется в фильтрах/джойнах. -- Обоснование: route_no — бизнес-идентификатор маршрута и часто используется в фильтрах/джойнах.
-- Использование route_no обеспечивает: -- Использование route_no обеспечивает:
+1 -1
View File
@@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS stg.seats (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: airplane_code -- Ключ распределения: airplane_code
-- Обоснование: airplane_code обеспечивает коллокацию seats ↔ airplanes при JOIN по airplane_code -- Обоснование: airplane_code обеспечивает коллокацию seats ↔ airplanes при JOIN по airplane_code
-- (в MPP это уменьшает вероятность перераспределения данных / motion). -- (в MPP это уменьшает вероятность перераспределения данных / motion).
+1 -1
View File
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS stg.segments (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: ticket_no -- Ключ распределения: ticket_no
-- Обоснование: ticket_no — это основной бизнес-ключ для билетов. -- Обоснование: ticket_no — это основной бизнес-ключ для билетов.
-- Использование ticket_no обеспечивает: -- Использование ticket_no обеспечивает:
+1 -1
View File
@@ -28,7 +28,7 @@ CREATE TABLE IF NOT EXISTS stg.tickets (
load_dttm TIMESTAMP NOT NULL DEFAULT now(), load_dttm TIMESTAMP NOT NULL DEFAULT now(),
batch_id TEXT batch_id TEXT
) )
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=1) WITH (appendonly=true, orientation=row, compresstype=zstd, compresslevel=1)
-- Ключ распределения: book_ref -- Ключ распределения: book_ref
-- Обоснование: book_ref — это основной бизнес-ключ для бронирований. -- Обоснование: book_ref — это основной бизнес-ключ для бронирований.
-- Использование book_ref обеспечивает: -- Использование book_ref обеспечивает: