Learning Platform
Глоссарий Troubleshooting
Урок 12.01 · 20 мин
Начальный
distributed computingshardingpartitioningparallel execution

В этом курсе мы много говорили о DWH, ETL, batch и streaming. Везде неявно предполагалось, что данные обрабатываются где-то — внутри Snowflake, в Spark-кластере, через Kafka. Но почему вообще нужны эти распределённые системы? Почему нельзя просто взять один большой сервер и считать на нём?

Этот урок — о фундаментальном ограничении одной машины и о трёх стратегиях, которые применяют, когда оно достигнуто. Без понимания этого вся остальная DE-инфраструктура (Spark, Kafka, Flink) кажется набором случайных инструментов.

Что такое single node и где его предел

Single node — это один сервер, на котором всё происходит: и CPU, и RAM, и диск. Самый понятный шаблон обработки: запустил Python-скрипт, прочитал CSV, посчитал агрегаты, сохранил результат.

Эта модель работает, пока данные помещаются на один узел. Под «помещаются» имеется в виду одно из трёх:

  • Данные помещаются в RAM (десятки гигабайт на одной машине).
  • Данные помещаются на диск, и обработка идёт стримом без полной загрузки в память.
  • Compute (CPU) одного узла справляется с задачей за разумное время.

Когда хотя бы одно из этих условий перестаёт выполняться, начинается распределённая обработка. Разберём все три ограничения по очереди.

Ограничение 1: данные больше памяти

В современных серверах RAM составляет десятки или сотни гигабайт. Самые мощные одиночные машины (AWS u-24tb1.metal или подобные) имеют до 24 терабайт RAM, но стоят сотни тысяч долларов в месяц.

Когда у тебя датасет 10 ТБ событий — он не помещается в RAM почти любого реалистичного сервера. Можно работать стримом с диском (читать порциями), но многие операции (например, joins, sort, hash aggregation) требуют держать в памяти промежуточный state. На диске они становятся в десятки раз медленнее.

Ограничение RAM на single node

С ростом данных мы упираемся сначала в RAM одной машины, потом в её диск, потом в её пропускную способность.

1 ГБлегкоСпокойно помещается в RAM ноутбука. Python, pandas, что угодно.
100 ГБсредний серверПомещается в RAM сервера среднего класса. Один shell-скрипт, может, со swap.
1 ТБдорогой серверПомещается в RAM очень дорогого одиночного сервера. Возможно, но дорого и хрупко.
10 ТБdistributedОдин сервер с 10 ТБ RAM существует, но стоит астрономические деньги. Дешевле — 10 машин по 1 ТБ или 100 по 100 ГБ.

При 10 ТБ дешевле и надёжнее взять 10 машин по 1 ТБ RAM и распределить нагрузку. Тогда у каждой машины свой кусок данных, помещающийся в её RAM, и обработка идёт параллельно.

Ограничение 2: CPU bottleneck

Даже если данные помещаются в RAM, может не хватать вычислительной мощности. CPU современного сервера — это десятки ядер (типично 32-128). Если задача — это серия независимых операций, она может загрузить все ядра. Но если объём вычислений настолько большой, что один сервер обрабатывает датасет 12 часов — это операционно неприемлемо.

Пример: ежедневный job, который считает рекомендации по 100 миллионам пользователей. Каждый пользователь требует расчёта похожести с 1000 кандидатов — это 100 миллиардов операций. На одной машине с 64 ядрами это часы. На 100 машинах по 64 ядра это минуты.

CPU bottleneck часто решается горизонтальным масштабированием: разбить данные на куски и обрабатывать параллельно на множестве машин. Это и есть распределённая обработка.

Ограничение 3: пропускная способность диска и сети

Третье ограничение менее очевидное. Современный SSD читает 1-5 ГБ/сек. NVMe — до 7-10 ГБ/сек. Это много, но 10 ТБ при скорости 5 ГБ/сек читаются 2000 секунд = 33 минуты. Если нужно прочитать датасет несколько раз (join по нескольким измерениям, агрегация по разным разрезам) — время растёт.

С распределённой обработкой эту проблему решают параллельным чтением: 100 машин одновременно читают свои куски данных с локальных дисков или из распределённой файловой системы (HDFS, S3). Эффективная пропускная способность складывается — 100 машин × 5 ГБ/сек = 500 ГБ/сек.

Аналогично сеть: одна машина 10 Gbit/sec прокачивает примерно 1 ГБ/сек. Между сотнями узлов в кластере — на порядки больше.

Три стратегии: sharding, partitioning, parallel execution

Когда мы решили, что обработка должна быть распределённой, нужно физически разнести данные и compute по узлам. Используются три фундаментальные техники:

Sharding (шардинг)

Шардинг — это разделение датасета на куски (shards), каждый из которых живёт на отдельном узле. Шардинг применяется обычно к persistent storage — операционным базам, key-value хранилищам, поисковым индексам.

Пользователи 1-100 000 000 разделены на 10 шардов:
shard 1: user_id [1, 10_000_000)         -> node A
shard 2: user_id [10_000_000, 20_000_000) -> node B
...
shard 10: user_id [90_000_000, 100_000_000) -> node J

Когда приходит запрос «дай пользователя с id 35 678 901», router считает 35_678_901 div 10_000_000 = 3, и направляет запрос на shard 4 (node D). Это хороший подход для point queries — поиска по конкретному ключу.

Partitioning (партиционирование)

Партиционирование — это разделение датасета на куски (partitions), часто по диапазону или хэшу ключа. В отличие от шардинга, партиции могут жить на одном узле или распределены по нескольким — это про логическое разделение данных, не обязательно по машинам.

Партиционирование особенно важно для больших таблиц в DWH и data lake:

fact_orders партиционируется по order_date:
  partition_2025_05_01/  -> файлы Parquet за 1 мая
  partition_2025_05_02/  -> файлы Parquet за 2 мая
  ...

Когда аналитик пишет SELECT * FROM fact_orders WHERE order_date = '2025-05-15', engine читает только одну партицию — не весь датасет. Это называется partition pruning и даёт огромный прирост скорости.

Sharding vs Partitioning

Шардинг — физическое разделение по узлам, обычно для OLTP и точечных запросов. Партиционирование — логическое разделение для аналитических запросов с фильтрами.

Shardingпо узлам кластераКаждый шард живёт на отдельной машине. Point query идёт на конкретную машину.
ПрименениеOLTP, KV-storesMongoDB, Cassandra, Elasticsearch, шардированный PostgreSQL.
Пример ключаhash(user_id) mod NХэш-функция от user_id определяет, на какой узел пойдут данные.
Partitioningпо диапазонам/ключамПартиции — это логические/физические куски таблицы, часто по дате или категории. Могут жить на одной или нескольких машинах.
ПрименениеOLAP, data lakeHive, Spark, Snowflake, BigQuery, Iceberg.
Примерorder_date = '2025-05-15'Партиция по дате — запрос с WHERE order_date читает только одну партицию.

В Spark/Hive обе техники используются одновременно: партиции по дате на storage-уровне + распределение compute по executor-ам кластера (это и есть «sharding на лету»).

Parallel execution

Параллельное выполнение — это разделение compute между множеством воркеров. У тебя есть Spark-кластер из 100 машин (executors). Когда приходит запрос, scheduler разбивает его на tasks — независимые единицы работы, каждая обрабатывает свою партицию данных.

# Псевдо-Spark
df.filter(col("country") == "RU") \
  .groupBy("category") \
  .agg(sum("amount").alias("revenue"))

Под капотом этот SQL превращается в DAG задач. 100 executors параллельно фильтруют свои партиции, группируют локально, потом обмениваются данными (shuffle) и считают финальный агрегат.

NOTE

Параллельное выполнение — это причина, почему Spark может прогнать 10 ТБ за минуты, а Python-скрипт — за дни. Каждая машина делает свою маленькую часть, и результаты складываются. Если задача хорошо параллелится, эффективная скорость растёт линейно с числом узлов (с некоторым потолком из-за coordination overhead).

Когда single node всё ещё достаточно

Распределённая обработка имеет свою цену: настройка кластера, координация задач, network shuffle, отладка распределённых багов. Поэтому распределённый стек оправдан не всегда.

Хорошие признаки, что single node всё ещё подходит:

  • Данные помещаются в RAM сервера (десятки ГБ).
  • Обработка укладывается в разумное время (минуты-часы).
  • Задача не критична к свежести (час задержки нормально).

В этих случаях обычный Python/pandas/DuckDB на одном сервере или ноутбуке работает быстрее, дешевле и проще, чем Spark-кластер. Многие DE-задачи 2026 года вообще не требуют распределённости — DuckDB и Polars прекрасно справляются с гигабайтами на ноутбуке.

Распределённый стек оправдан, когда:

  • Данные не помещаются в RAM/диск одной машины (терабайты, петабайты).
  • Обработка одной машиной заняла бы дни/недели.
  • Параллелизм требуется для SLA (job должен укладываться в 30 минут).
  • Нужна fault tolerance — задача должна продолжать работать при падении узлов.

Cloud DWH vs Spark: разные подходы к распределённости

Стоит уточнить: распределённость бывает разной. Snowflake и BigQuery — это распределённые системы, но они скрывают это от пользователя. Ты пишешь SQL, движок сам разбивает на задачи и распределяет по узлам. Никакой ручной настройки кластера.

Spark и Flink — это открытые распределённые системы. Ты явно конфигурируешь кластер, выбираешь количество executors, размер партиций, стратегии join’ов. Это даёт больше контроля и больше боли.

Оба подхода правильны для разных сценариев. SQL-аналитика в DWH — Snowflake/BigQuery. Программируемые пайплайны с custom-логикой — Spark. Streaming — Flink/Spark Structured Streaming.

Spark: архитектура distributed compute — driver, executors, partitions

Попробуй сам

Возьми любой датасет — например, NYC taxi trips (около 200 ГБ за год). Подумай, на чём ты его будешь обрабатывать. Один экземпляр DuckDB на сервере 64 ГБ RAM справится за часы? Snowflake X-Small warehouse за минуты? Spark-кластер из 10 узлов за минуты? Какие плюсы и минусы у каждого варианта? Это упражнение и есть мышление DE — каждая задача требует выбора правильного инструмента, а не самого мощного.

Проверка знанийKnowledge check
Какие три фундаментальных ограничения single node заставляют переходить к распределённой обработке?
ОтветAnswer
Первое — данные не помещаются в RAM или на диск одной машины: при объёмах десятков терабайт даже самый дорогой одиночный сервер становится бутылочным горлышком. Второе — CPU bottleneck: обработка одним сервером занимает дни вместо разумных часов или минут, требуемых бизнесом. Третье — пропускная способность диска и сети: read/write скорости одной машины ограничены, и параллельное чтение с сотен дисков складывается в эффективную пропускную способность на порядки выше. Когда хотя бы одно из этих ограничений становится критичным, переходят к распределённой обработке — sharding для OLTP, partitioning + parallel execution для OLAP и data lake.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какое утверждение про single node обработку верно?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4