Files
mini-lakehouse-lab/notebooks/02_lakehouse_mental_model.ipynb
T
ddadmin 91a5c85469 fix(spark): исправлено подключение ноутбуков к кластеру
- Зачем: модули 2–8 в новом процессе выбирали local[*] вместо Spark-кластера.
- Что: master перенесён в общий spark-defaults.conf, smoke требует точный адрес кластера, документация и планы синхронизированы.
- Проверка: фактическая инициализация всех восьми ноутбуков, повторный getOrCreate, распределённые вычисления, Iceberg/MinIO write-read-cleanup и docker compose config прошли.
2026-09-15 08:18:33 -04:00

13 KiB

Модуль 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 [ ]:
# Адрес Spark-кластера и настройки 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 [ ]:
# PURGE удаляет одноразовые таблицы и из каталога, и из хранилища
spark.sql("DROP TABLE IF EXISTS lakehouse.module_02.demo_table PURGE")
spark.sql("DROP TABLE IF EXISTS lakehouse.module_02.my_first_table PURGE")

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

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