Управление схемами и миграция с помощью SQLAlchemy и Alembic
Это руководство описывает, как управлять схемами Selena с использованием экосистемы Python — включая SQLAlchemy, Alembic и sqlacodegen — через диалект selena SQLAlchemy. Оно помогает понять, почему миграция схем полезна и как эффективно использовать её с Selena.
Обзор
Многие пользователи управляют таблицами, представлениями и материализованными представлениями Selena напрямую с помощью SQL DDL. Однако по мере роста проектов ручное со провождение операторов ALTER TABLE становится подверженным ошибкам и сложным для отслеживания.
Диалект Selena SQLAlchemy (selena) предоставляет:
- Полноценный слой моделей SQLAlchemy для таблиц, представлений и материализованных представлений Selena
- Декларативные определения для схем таблиц и свойств таблиц (включая представления и материализованные представления)
- Интеграцию с Alembic для автоматического обнаружения и генерации изменений схемы
- Совместимость с инструментами типа sqlacodegen для обратной генерации моделей
Это позволяет пользователям Python поддерживать схемы Selena декларативным, версионируемым и автоматизированным способом.
Ключевые преимущества
Хотя миграция схем традиционно ассоциируется с OLTP базами данных, она также ценна в системах хранилищ данных, таких как Selena. Команды используют Alembic вместе с диалектом Selena благодаря преимуществам, перечисленным ниже.
Декларативное определение схемы
После того как вы определите схему в моделях Python ORM или в стиле SQLAlchemy core, вам больше не требуется вручную писать операторы ALTER TABLE.
Автоматическое сравнение и автогенерация
Alembic сравнивает текущую схему Selena с вашими моделями SQLAlchemy и автоматически генерирует скрипты миграции (CREATE/DROP/ALTER).
Проверяемые, версионируемые миграции
Каждое изменение схемы становится файлом миграции (Python), поэтому пользователи могут отслеживать изменения и откатывать их при необходимости.
Единообразный workflow для всех окружений
Изменения схемы можно применять к development, staging и production с использованием одного и того же процесса.
Установка и подключение
Предварительные требования**
- Python-клиент Selena: 1.3.2 или новее
SQLAlchemy: 1.4 или новее (рекомендуется SQLAlchemy 2.0; требуется для использованияsqlacodegen)Alembic: 1.16 или новее
Установка Python-клиента Selena
Выполните следующую команду для установки Python-клиента Selena.
pip install selena
Подключение к Selena
Подключитесь к вашему cluster Selena с использованием следующего URL.
selena://<user>:<password>@<FE_host>:<query_port>/[<catalog>.]<database>
user: Имя пользовател я для подключения к cluster.password: Пароль пользователя.FE_host: IP-адрес FE.query_port:query_portFE (по умолчанию: 9030).catalog: Имя catalog, в котором находится ваша база данных.database: Имя базы данных, к которой вы хотите подключиться.
После установки вы можете быстро проверить подключение с помощью следующего примера кода:
from sqlalchemy import create_engine, text
# сначала нужно создать `mydatabase`
engine = create_engine("selena://root@localhost:9030/mydatabase")
with engine.connect() as conn:
conn.execute(text("SELECT 1")).fetchall()
print("Connection successful!")
Определение моделей Selena (декларативный ORM)
Диалект Selena поддерживает:
- Таблицы
- Представления
- Материализованные представления
Он также поддерживает специфичные для Selena атрибуты таблиц, такие как:
ENGINE(OLAP)- Модели ключей (
DUPLICATE KEY,PRIMARY KEY,UNIQUE KEY,AGGREGATE KEY) - Варианты
PARTITION BY(RANGE / LIST / Expression partitioning) - Варианты
DISTRIBUTED BY(HASH / RANDOM) ORDER BY- Свойства таблиц (например,
replication_num,storage_medium)
- Опции диалекта Selena передаются как именованные аргументы с префиксом
selena_. - Префикс
selena_должен быть в нижнем регистре. Суффикс принимается в любом регистре (например,PRIMARY_KEYиprimary_key). - Если вы указываете ключ таблицы (например,
selena_primary_key="id"), задействованные колонки также должны быть помеченыprimary_key=TrueвColumn(...), чтобы метаданные SQLAlchemy и автогенерация Alembic работали корректно.
Примеры ниже отражают реальный публичный API и имена параметров.
Пример таблицы
Опции таблиц Selena могут быть указаны как в стиле ORM (через __table_args__), так и в стиле Core (через Table(..., selena_...=...)).
Стиль ORM (декларативный)
from sqlalchemy import create_engine
from sqlalchemy.orm import Mapped, declarative_base, mapped_column
from selena import INTEGER, STRING
# используем тот же engine, что и в быстром тесте
engine = create_engine("selena://root@localhost:9030/mydatabase")
Base = declarative_base()
class MyTable(Base):
__tablename__ = 'my_orm_table'
id: Mapped[int] = mapped_column(INTEGER, primary_key=True)
name: Mapped[str] = mapped_column(STRING)
__table_args__ = {
'comment': 'table comment',
'selena_primary_key': 'id',
'selena_distributed_by': 'HASH(id) BUCKETS 10',
'selena_properties': {'replication_num': '1'}
}
# Создание таблицы в базе данных
Base.metadata.create_all(engine)
Стиль Core
from sqlalchemy import Column, MetaData, Table, create_engine
from selena import INTEGER, VARCHAR
# используем тот же engine, что и в быстром тесте
engine = create_engine("selena://root@localhost:9030/mydatabase")
metadata = MetaData()
my_core_table = Table(
'my_core_table',
metadata,
Column('id', INTEGER, primary_key=True),
Column('name', VARCHAR(50)),
# Специфичные для Selena аргументы
selena_primary_key='id',
selena_distributed_by='HASH(id) BUCKETS 10',
selena_properties={"replication_num": "1"}
)
# Создание таблицы в базе данных
metadata.create_all(engine)
Для полного справочника по атрибутам таблиц и типам данных см. Справочник [4].
Пример представления
Ниже приведен рекомендуемый стиль определения представления, использующий columns как список словарей (name/comment). Этот пример основан на существующей таблице my_core_table.
from selena.schema import View
# Повторно используем metadata из примера Core-таблицы выше
metadata = my_core_table.metadata
user_view = View(
"user_view",
metadata,
definition="SELECT id, name FROM my_core_table WHERE name IS NOT NULL",
columns=[
{"name": "id", "comment": "ID"},
{"name": "name", "comment": "Name"},
],
comment="Active users",
)
Для получения дополнительной информации об опциях View и ограничениях см. Справочник [5].
Пример материализованного представления
Материализованные представления определяются аналогично. Свойство selena_refresh — это строка синтаксиса, указывающая стратегию обновления.
from selena.schema import MaterializedView
# Повторно используем metadata из примера Core-таблицы выше
metadata = my_core_table.metadata
# Создание простого материализованного представления (асинхронное обновление)
user_stats_ = MaterializedView(
'user_stats_',
metadata,
definition='SELECT id, COUNT(*) AS cnt FROM my_core_table GROUP BY id',
selena_refresh='ASYNC'
)
Для получения дополнительной информации об опциях и ограничениях ALTER см. Справочник [6].
Интеграция с Alembic
Диалект Selena SQLAlchemy обеспечивает полную поддержку:
- Создание / удаление таблицы
- Создание / удаление представления
- Создание / удаление материализованного представления
- Обнаружение поддерживаемых изменений в специфичных для Selena атрибутах (например, свойства таблиц и распределение)
Это позволяет корректно работать автогенерации Alembic.
Инициализация Alembic
-
Инициализируйте Alembic:
alembic init migrations -
Настройте URL вашей базы данных в
alembic.ini:# alembic.ini
sqlalchemy.url = selena://<user>:<password>@<FE_host>:<query_port>/[<catalog>.]<database> -
Включите логирование диалекта Selena (опционально):
Вы можете включить логгер
selenaвalembic.ini, чтобы отслеживать обнаруженные изменения таблицы через логи. Подробнее см. Справочник [2].Отредактируйте
env.py(настройте оба пути — offline и online):from alembic import context
from selena.alembic import render_column_type, include_object_for_view_
from selena.alembic.selena import SelenaImpl # noqa: F401 (обеспечиваем регистрацию impl)
from myapp.models import Base # настройте под ваш проект
target_metadata = Base.metadata
def run_migrations_offline() -> None:
url = context.config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
render_item=render_column_type,
include_object=include_object_for_view_
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
# ... создание engine и подключение как в стандартном env.py из alembic ...
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
render_item=render_column_type,
include_object=include_object_for_view_
)
with context.begin_transaction():
context.run_migrations()
Автоматическая генерация миграций
alembic revision --autogenerate -m "initial schema"
Alembic сравнит модели SQLAlchemy с фактической схемой Selena и выведет корректный DDL.
Применение миграций
alembic upgrade head
Откат также поддерживается (где это возможно).
DDL в Selena не транзакционен для нескольких операторов. Если upgrade прерывается, вам может потребоваться проверить, что уже было применено, и выполнить ручное исправление (например, написать компенсирующую миграцию или выполнить ручной DDL) перед повторным запуском.