Files
mini-lakehouse-lab/notebooks/02_lakehouse_mental_model.ipynb
T
ddadmin 3763538aca feat(notebooks): реализован ноутбук Модуля 2 «Ментальная модель Lakehouse»
- Зачем:
  - необходимо дать студенту практический инструмент для понимания разделения storage, catalog и compute.
- Что:
  - создан notebooks/02_lakehouse_mental_model.ipynb с демонстрацией записи в Spark и инспекцией в PostgreSQL и MinIO.
  - в ноутбук добавлена таблица сравнения Lakehouse vs классические СУБД.
  - реализованы механизмы идемпотентности (CREATE OR REPLACE TABLE) и автоматической очистки ресурсов.
  - обновлен статус Модуля 2 в планах на "Ready for validation".
- Проверка:
  - проверена структура JSON ноутбука, наличие всех демонстрационных и самостоятельных ячеек, а также корректность путей к MinIO (prefix warehouse/).
2026-03-07 01:26:19 +03:00

13 KiB
Raw Blame History

Модуль 2. Ментальная модель Lakehouse: storage, catalog, compute

В этом модуле мы на практике разберем, чем Lakehouse отличается от классических реляционных СУБД (PostgreSQL, Greenplum). В классической СУБД данные, метаданные и вычислитель жестко связаны внутри одной системы. В Lakehouse эти слои физически разделены:

  • Storage (Хранилище): где лежат физические файлы (у нас это MinIO / S3).
  • Catalog (Каталог): где хранится информация о таблицах и их расположении (у нас это PostgreSQL).
  • Compute (Вычислитель): кто обрабатывает данные (у нас это Spark, а позже добавится Trino).
PostgreSQL / Greenplum Lakehouse
Данные Внутри СУБД (pg_data) Файлы в объектном хранилище (MinIO)
Метаданные Системные каталоги (pg_catalog) Внешний каталог (PostgreSQL JDBC)
Вычислитель Тот же процесс СУБД Независимые движки (Spark, Trino)
In [ ]:
# Мы не указываем .master() явно, так как он и другие настройки Iceberg
# подтягиваются из конфигурации spark-defaults.conf при старте сессии.
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("Module-02-Mental-Model") \
    .getOrCreate()

# Spark выводит много INFO/WARN логов — убираем их, чтобы не засорять вывод ноутбука
spark.sparkContext.setLogLevel("ERROR")
print("Spark сессия готова!")

1. Запись таблицы (Compute)

Мы используем Spark (вычислитель) для создания таблицы и записи данных.

In [ ]:
# Создаем namespace (логическую базу данных)
spark.sql("CREATE NAMESPACE IF NOT EXISTS lakehouse.module_02")

# Создаем или перезаписываем таблицу и вставляем пару строк
spark.sql("""
CREATE OR REPLACE TABLE lakehouse.module_02.demo_table (
    id int,
    name string,
    created_at timestamp
)
USING iceberg
""")

spark.sql("""
INSERT INTO lakehouse.module_02.demo_table 
VALUES 
    (1, 'Alice', current_timestamp()),
    (2, 'Bob', current_timestamp())
""")

# Прочитаем данные через Spark, чтобы убедиться, что они записались
spark.sql("SELECT * FROM lakehouse.module_02.demo_table").show()

2. Где живут метаданные? (Catalog)

Таблица в Lakehouse — это не просто файлы. Чтобы Spark (или Trino) знал, где лежат данные и какая у них схема, используется Catalog. В нашем стенде это PostgreSQL. Посмотрим, какая запись появилась в базе данных каталога.

In [ ]:
import psycopg2
import pandas as pd
import warnings

# Подключаемся к PostgreSQL, который хранит метаданные Iceberg (наш Catalog)
with psycopg2.connect(
    host="postgres-iceberg",
    database="iceberg",
    user="iceberg",
    password="iceberg"
) as conn:
    # Запрашиваем информацию о нашей таблице
    query = """
    SELECT table_namespace, table_name, metadata_location, previous_metadata_location 
    FROM iceberg_tables 
    WHERE table_namespace = 'module_02'
    """

    # Используем catch_warnings локально, чтобы скрыть UserWarning от pandas при чтении из DB-API
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', UserWarning)
        df_catalog = pd.read_sql(query, conn)

# Посмотрим, куда указывает catalog (обратите внимание на metadata_location)
df_catalog

В поле metadata_location хранится путь к актуальному файлу метаданных таблицы в физическом хранилище.

3. Где живут сами данные? (Storage)

В качестве физического хранилища (Storage) мы используем MinIO — S3-совместимое объектное хранилище. Данные и файлы метаданных лежат там. Посмотрим на физические артефакты нашей таблицы.

In [ ]:
import boto3

# Подключаемся к MinIO
s3 = boto3.client(
    's3',
    endpoint_url='http://minio:9000',
    aws_access_key_id='minioadmin',
    aws_secret_access_key='minioadmin'
)

bucket_name = 'lakehouse'
# Путь (prefix) начинается с warehouse, так как это задано в настройках каталога
prefix = 'warehouse/module_02/demo_table/'

# Получаем список всех файлов (объектов), относящихся к нашей таблице
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)

print("Файлы таблицы в MinIO:\n")
contents = response.get('Contents', [])
if not contents:
    print("Файлы не найдены. Проверьте prefix или убедитесь, что таблица создана.")
else:
    for obj in contents:
        key = obj['Key']
        size_kb = obj['Size'] / 1024
        if '/data/' in key:
            print(f"[DATA] {key} ({size_kb:.2f} KB)")
        elif '/metadata/' in key:
            print(f"[META] {key} ({size_kb:.2f} KB)")
        else:
            print(f"[OTHER] {key} ({size_kb:.2f} KB)")

Что мы видим?

  1. Папка metadata/ содержит файлы .json (снимки состояния таблицы), .avro (манифесты, описывающие, какие файлы данных актуальны).
  2. Папка data/ содержит сами данные в формате .parquet.

Подумайте: Что произойдет, если вы удалите файлы из MinIO, но не удалите запись в каталоге? А что если наоборот?


4. Самостоятельное задание

  1. Создайте таблицу lakehouse.module_02.my_first_table.
  2. Запишите в неё несколько произвольных строк.
  3. Прочитайте таблицу через Spark.
  4. Выполните запросы к PostgreSQL и MinIO (аналогично примерам выше), чтобы найти metadata_location и физические parquet файлы вашей новой таблицы.
In [ ]:
# Ваш код: создание таблицы my_first_table и вставка данных

In [ ]:
# Ваш код: чтение данных через Spark

In [ ]:
# Ваш код: инспекция каталога (PostgreSQL)

In [ ]:
# Ваш код: инспекция хранилища (MinIO)


Checkpoint

Убедитесь, что вы можете ответить на следующие вопросы:

  • Можете ли вы показать конкретные файлы данных и метаданных созданной таблицы в MinIO (через UI MinIO на порту 9001 или через код выше)?
  • Можете ли вы показать запись о таблице в PostgreSQL и объяснить, на что указывает поле metadata_location?
  • Что произойдёт, если удалить файлы из MinIO, но не трогать каталог (или наоборот)?
  • Понимаете ли вы теперь разницу между storage (MinIO) и catalog (PostgreSQL)?

5. Очистка ресурсов (Cleanup)

Выполните ячейку ниже, чтобы удалить созданные таблицы и namespace. Это необходимо, чтобы не оставлять "мусор" перед переходом к следующим модулям.

In [ ]:
# Удаляем таблицы (это удалит их и из каталога, и из хранилища)
spark.sql("DROP TABLE IF EXISTS lakehouse.module_02.demo_table")
spark.sql("DROP TABLE IF EXISTS lakehouse.module_02.my_first_table")

# Удаляем namespace
spark.sql("DROP NAMESPACE IF EXISTS lakehouse.module_02")

spark.stop()
print("Очистка завершена, Spark сессия остановлена.")