Привет, Хабр!

Недавно я понял, что не знаю, что такое Hadoop.

(На этом моменте становится понятно, что данная статья ориентирована на людей, которые не имеют экспертизы и реального опыта взаимодействия с продуктами экосистемы Hadoop)

Сам я являюсь разработчиком, и ежедневно взаимодействую с различными СУБД – в основном, с пресловутой PostgreSQL. Каково же было мое удивление, когда я узнал, что на проде в эту БД данные попадают не напрямую – а с какого-то Greenplum, а туда они, в свою очередь, приходят с некоего Hadoop.

В этот момент я решил узнать, чем обоснована необходимость использования этих инструментов и что они из себя представляют.

Я пишу этот материал в процессе прояснения темы для самого себя, поэтому можно относится к этой статье, как к моему краткому конспекту. Также настойчиво прошу поправлять меня в комментариях, если я где-то был неправ или недостаточно точен.

Итак, поехали.

Проблематика, которую решает Hadoop

Каждый ребенок знает, что традиционные базы данных реляционного формата – это круто. Это понятно и абсолютно очевидно. Я делаю небольшое допущение, что для решения большинства задач, которые стоят перед разработчиком, нам необходимо взаимодействовать именно с традиционными СУБД (ну типа postgres, oracle, mysql и тд и тп).

Давайте попробуем представить, что все данные, которые используются в системе, лежат именно в виде табличек. Назревает вопрос: так зачем вообще нужен Hadoop, если у нас есть какой-нибудь большой и крутой оракл/постгрес/вставьтенужное?
Казалось бы, складываем все данные в него, и всё. Дешево и сердито.

Однако, как оказалось, не дешево.

Практически любая традиционная СУБД (особенно postgres) масштабируется вертикально – а это значит, что при росте объема данных нам нужно больше CPU/RAM/SSD на одну машину (что будет очень дорого при больших объемах данных, и более того, даже с бесконечным бюджетом будет иметь физические пределы).

Кроме того, если мы хотим работать с по-настоящему огромными объемами данных, любая вышеупомянутая СУБД рано или поздно намертво встанет, если не танцевать над ней с бубном. Аналитические запросы будут отрабатывать недопустимое время, если речь идет о петабайтах данных.

Вот представим кейс: мы – крупный банк, и ежедневно обрабатываем 1 терабайт данных (цифра вполне себе реальная) операций наших клиентов, а кроме того, храним информацию о самих клиентах и их счетах.

А при обработке каждой операции мы должны подтянуть данные об отправителе, получателе, проверить все действо на законность и тд и тп.

Росфинмониторинг диктует нам правила: хранить документы и сведения по клиенту и операциям не менее 5 лет (обычно — с даты прекращения отношений с клиентом либо в случаях с операциями — от даты операции).

Ну, объемы информации, с которой нужно будет работать, можете прикинуть сами.

Разумеется, данные об операции клиента 4х летней давности статистически пригождаются крайне редко – поэтому они будут висеть мертвым грузом в нашей БД и сильно замедлять обработку всех запросов. Короче:

Горячие данные — часто и/или срочно запрашиваемые: требуют низкой задержки (миллисекунды — доли секунды) и высокой доступности. Примеры: текущие балансы, незавершённые транзакции, сессии пользователей, кэши.

Холодные данные — редко запрашиваемые, нужны для аналитики/аудита/истории; допустима большая задержка (секунды–минуты, иногда часы). Примеры: архивы транзакций за прошлые годы, старые выписки, старые логи.

Внимательный читатель скажет, что проблема хранения решается партиционированием и будет отчасти прав.

Однако существует и вторая проблема: необходимость обработки этих данных. В том случае, если мы захотим обработать данные, хранящиеся в разных партициях/на разных нодах, мы упремся в то, что «бутылочным горлышком» является процессор самой СУБД, который будет пытаться последовательно обработать все данные – предварительно достав их из распределенного хранилища. Очевидно, что на больших объемах данных это очень неэффективно по времени и ресурсам.

Вот бы можно было все эти петабайты данных просто закинуть файликами на какие-нибудь старенькие накопители на несколько серверов не первой свежести, и чтобы в любой момент была возможность быстро обработать этот огромный объем данных…

Тут то на сцену и выходит Hadoop – и говорит, дружище – складывай файлики любых размеров на любое количество накопителей, а я тебе если что помогу быстро эти данные обработать и вытащить – например, построить табличку по всем операциям Иванова за последние 5 лет.

Терминология

Самое время сделать небольшую ремарку: мне (и не только мне, полмиллиона просмотров и тысяча закладок) очень понравилась вот эта статья про Hadoop и его компоненты. Написана очень простым и понятным языком, по-доброму. Здесь я стремлюсь повторить у вас те же чувства, что я испытал сам после прочтения той статьи. Рекомендую ознакомится.

Раскроем значения некоторых терминов и аббривеатур:

HDFS

Яндекс GPT утверждает, что HDFS a.k.a. Hadoop Distributed File System — это распределённая файловая система, предназначенная для работы с большими данными в экосистеме Hadoop на распределённых кластерах.

Отечественная LLM поясняет, что HDFS разбивает файлы на блоки (по умолчанию размером 128 МБ) и хранит их на разных узлах кластера. Это обеспечивает параллельную обработку данных и высокую скорость доступа. А для обеспечения отказоустойчивости каждый блок дублируется на несколько узлов. Если один узел выходит из строя, данные восстанавливаются с других узлов. 

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

Картинка из интернета
Картинка из интернета

Глядя на картинку из интернета, видно, что некая NameNode является обязательной частью обработки любого запроса с клиента. Это нода кластера, которая хранит информацию (метаданные) о том, на каких DataNode хранится файл. Поэтому планировщик (механизм, отвечающий за распараллеливание и запуск процессов, извлекающих данные) спрашивает у NameNode, по каким DataNode раскидан файл, и собирает его в параллели с нескольких нод.

Ну и соответственно общая скорость отдачи данных близится к скорости каждого диска * кол-во дисков. Это позволяет нам почти до бесконечности масштабировать вычислительные мощности кластера. В случае с одной огромной нодой такое, как вы понимаете, невозможно.

Map Reduce

Данные нам необходимо не только сохранять и извлекать, но и трансформировать. А для какой-либо обработки данных, которые раскиданы в распределенном хранилище, нам нужен план того, как мы будем это делать.

Итак, MapReduce — это программная модель и подход к обработке больших данных, придуманный для параллельной обработки очень больших файлов на кластере машин. MapReduce разбивает задачу на две простые фазы: Map (преобразование) и Reduce (агрегация).

Есть еще этап сортировки, и с ним процесс обработки данных можно описать следующим образом:

  1. Map: берём входные данные (файлы), каждый файл обрабатываем локально и превращаем в набор пар ключ → значение (key → value).

  2. Shuffle/Sort: группируем по ключам — все значения с одним ключом собираются вместе (это шаг, который выполняет обмен данными между узлами).

  3. Reduce: для каждого ключа применяем функцию, которая агрегирует список значений в итог (например, суммирует).

В качестве примера можно привести избитый алгоритм подсчёта слов в тексте:

Map: Получаем строку текста и выдаём пары (слово, 1) для каждого слова в строке.

Shuffle: Собираем все кортежи вместе(группируем значения (наши единички) по ключу – самому слову).

Reduce: Суммируем значения по ключу (например, для "hello" получаем список [1,1,1,...] и превращаем его в (hello, 42) ).

На выходе получаем словарик ключ-значение, с тем, сколько раз каждое слово встречается в тексте. Прелесть в том, что первый этап (Map) может проходить параллельно для каждой строчки из текста (в реальности – на каждой ноде, где лежат данные), а также то, что сами данные никуда не переносятся, а обрабатываются локально (на первом этапе расчета, разумеется).

Также MapReduce называют одноименную реализацию данной модели вычислений в экосистеме Hadoop.

YARN

Про YARN скажу совсем кратенько: YARN (Yet Another Resource Negotiator) — это менеджер ресурсов Hadoop-кластера, который распределяет CPU и память в виде контейнеров между приложениями (Spark, MapReduce, Tez и др.). Он позволяет нескольким фреймворкам безопасно делить один кластер, обеспечивает учёт и изоляцию ресурсов, а также применяет политики планирования (очереди/квоты) для соблюдения SLA.

Внутри этого менеджера ресурсов есть несколько компонентов:

  • ResourceManager — мастер-компонент, отвечает за глобальное распределение ресурсов по приложениям и очередям. Ничего не знает про выполняемые на контейнерах задачи.

  • NodeManager — агент на каждом узле: запускает/мониторит контейнеры, отчитывается ResourceManager о состоянии узла.

  • ApplicationMaster — компонент, который договаривается с RM о контейнерах для конкретного приложения и управляет его задачами (MapReduce джобами, Spark-on-YARN приложениями и т.п.).

Картинка из интернета #2
Картинка из интернета #2

И очень упрощенно процесс обработки можно описать так:

  1. Клиент подаёт заявку на запуск приложения (например Spark job).

  2. ResourceManager запускает ApplicationMaster для этого приложения.

  3. ApplicationMaster запрашивает у ResourceManager контейнеры (с указанием памяти/CPU/локальных предпочтений).

  4. ResourceManager выделяет контейнеры на NodeManagers, а ApplicationMaster запускает задачи внутри этих контейнеров.

  5. NodeManagers следят за здоровьем и отчитываются ResourceManager'у.

Hadoop

Настал момент, когда мы можем приблизительно описать, что такое хадуп:

Hadoop — это платформа для дешёвого масштабируемого хранения и параллельной обработки больших данных, где HDFS отвечает за надёжное распределённое хранение, YARN — за управление ресурсами, а обширная экосистема (MapReduce, Spark, Hive и пр.) обеспечивает ETL и интеграцию со сторонними системами.

Потрясающая аналогия от chatGpt v5 – с растерзанными книгами, раскиданными по библиотеке, и читателями, желающими читать все главы книги параллельно.
Потрясающая аналогия от chatGpt v5 – с растерзанными книгами, раскиданными по библиотеке, и читателями, желающими читать все главы книги параллельно.

Закрепим: чем же хорош Hadoop? Он позволяет не тягать огромные объемы данных куда-либо для обработки. Он хранит их на кластере дешевых машин, и умеет обрабатывать любые запросы к этим данным параллельно, запуская вычисления на каждой машине (и на каждом кусочке хранимых на ней данных) отдельно – и затем агрегировать и выдавать результат. Также Hadoop отлично реплицирует данные, обеспечивая отличную отказоустойчивость. Это позволяет горизонтально масштабировать ваш кластер очень и очень долго, получая при этом отличную производительность.

Spark

Apache Spark — фреймворк с открытым исходным кодом для распределённой обработки данных. Фреймворк входит в экосистему проектов Hadoop, хотя и является самостоятельным продуктом, и может использоваться и без Hadoop.

Spark – это штука, которая обрабатывает данные на распределенных кластерах. Пока по описанию очень похоже на Hadoop MapReduce, да?

По сути, этот фреймворк – более эффективная замена MapReduce. Основные отличия можно описать так:

MapReduce — это жёсткая модель: Map → Shuffle → Reduce, и промежуточные результаты между шагами обычно пишутся на диск.
Spark — универсальный движок, который строит произвольный граф выполняемых операций, старается по максимуму использовать оперативную память, а не диск, а также оптимизирует план выполнения сложных задач.

Picture background
Картинка из интернета #3

Из-за этого Spark почти во всех реальных задачах работает гораздо быстрее и эффективнее.

Кроме того, Spark адаптирован под несколько ЯП: Python (PySpark), Scala (родной язык Spark), Java, R (SparkR), а также Spark SQL.

Row-layout формат

Форматы хранения данных на диске и их вычитки определенно не являются главной вещью, которую я хотел освещать в статье, однако я подумал, что про row/column-based формат хранения данных обязательно необходимо упомянуть, потому что данные, хранящиеся в Hadoop кластере, не предназначены для OLTP формата взаимодействия. А в случае с аналитическими запросами колоночный формат хранения данных очень часто используется на практике (parquet, orc, СУБД ClickHouse, MPP Greenplum).

Picture background
Картинка из интернета #4

Итак, для начала рассмотрим привычный нам, строковый формат хранения данных (таблиц). При таком формате записи в таблице сохраняются на диске построчно. Например :

[1,Anna,100.0,2025-01-01]
[2,Boris,50.0,2025-01-02]
[3,Claire,200.0,2025-01-03]

Такие блоки хранятся на диске последовательно. Соответственно в сценариях, в которых нам нужно вытащить запись по ключу и прочитать много ее полей, либо обновить какие-то из них – эффективнее будет именно такой формат. Вытащили запись, и изменяем ее – параллельно другая транзакция может работать с другой записью. С точки зрения диска это будет гораздо быстрее, чем колоночный формат.

Column-layout формат

При колоночном подходе мы храним каждый столбец таблицы на диске отдельно. Например:

id: [1,2,3]
name: [Anna,Boris,Claire] 
amount: [100.0,50.0,200.0] 
ts: [2025-01-01, 2025-01-02, 2025-01-03]

Теперь уже каждая колонка сохраняется на диске последовательно. Здесь будут эффективно отрабатывать случаи, когда из большой таблицы с множеством колонок нам надо извлечь только значения только некоторых колонок, например: SELECT id, amount FROM table WHERE ts BETWEEN '2025-01-01' AND '2025-01-31' – в таком случае можно прочитать только колонки ts (для фильтра) и id, amount (для результата), а остальные колонки (если представить, что в нашей таблице их под сотню) не читать вообще.

Соответственно в большинстве случаев мы можем читать только те данные, которые у нас запросили – это круто и удобно. Также становится гораздо удобнее сжимать данные, когда они хранятся по колонкам: status[SUCCESS, SUCCESS, SUCCESS, SUCCESS, ERROR, ERROR…]

Однако везде есть свои минусы. В данном случае в минусы будут записаны почти все плюсы row-layout формата. Т.е.:

Такой формат не применим к кейсам, где необходимо обрабатывать транзакционные операции с низкой латентностью (которые должны завершиться быстро: например, списание денег со счета). В таком случае мы готовы пожертвовать лишней памятью и вычитать лишние данные. Нам гораздо важнее, что найденная строка будет содержать сразу все в одном месте. Нам не надо будет бегать по разным местам диска и собирать нужную строку по кусочкам (см. random reads).

Также column-layout формат не применим к ситуациям, где требуются быстрые небольшие записи/обновления – по тем же причинам: скажем нам надо вычитать ключ из одного места на диске, пойти в другое, найти там нужную колонку – и записать в нее обновленное значение.

Имеет свое место и исторический контекст: под OLTP исторически заточен именно row-layout формат: эффективные механизмы блокировок, MVCC, быстрые коммиты. При тысячах мелких транзакций в секунду row-store обычно даёт стабильную низкую латентность. Column-store же (насколько мне известно) больше оптимизирован под пакетные обработки (например, батчевые записи), и помогает уменьшить нагрузку на диск (что при работе с огромными объемами данных дает значительный прирост к скорости).

Выводы

Когда использовать Hadoop?

Когда данных ОЧЕНЬ много, и надо где-то хранить и обрабатывать (в OLAP режиме) ОЧЕНЬ большие объемы быстро. При этом объемы настолько большие, что обработка на одной машине, даже очень мощной, невозможна и требуются распределенные многопоточные вычисления.

Когда использовать реляционную СУБД?

Когда мы работаем в OLTP формате и нам нужен быстрый отклик для получения «горячих» данных. Когда нам нужна строгая ACID-консистентность и простая интеграция с существующими приложениями.

Да и вообще: если ты точно не знаешь, что тебе использовать – используй реляционную СУБД.

Когда появляется необходимость работать с реляционными представлениями поверх Hadoop?

Когда нужно обеспечить возможность запроса данных на Hadoop кластер в SQL-формате, и при этом такие запросы тяжелые и не частые (например, перестраивать витрину данных раз в сутки, либо руками выполнять аналитические запросы).

Кстати, есть даже СУБД, работающие поверх Hadoop (Hive, HBase, Cassandra и др.) – если есть желание, рекомендую ознакомится.

Важное предупреждение

Здесь, уже после того, как мы поняли, что такое Hadoop, я хотел бы сделать лирическое отступление: скорее всего, вам не нужен Hadoop для решения ваших задач. Вы не гугл.

“MapReduce и Hadoop в этом плане являются лёгкими мишенями для критики, ведь даже последователи карго-культа со временем признали, что их обряды как-то не очень работают для привлечения самолётов.
Но это наблюдение можно и обобщить: если вы решаете использовать какую-то технологию, созданную большой корпорацией, вполне может быть, что вы не пришли к этому решению сознательно; вполне возможно, что к этому привела какая-то мистическая вера в то, что можно имитировать поведение гигантов и таким образом достичь тех же высот.”

Вот здесь приведен яркий пример сказанного выше. Очень часто люди стремятся применить для решения своих задач классный инструмент, но не задумываются о том, какую проблему они действительно решают, и какие есть альтернативные пути ее решения. В статье, уже упомянутой выше, есть чек-лист, с которым стоит сверяться при подборе инструментария для решения любой крупной проблемы. Но это я так, к слову, чтобы вдруг впечатленный читатель не побежал решать все задачи на новом, прикольном стеке.

Для себя я сделал вывод, что мне, как разработчику, становится экспертом в Hadoop и уметь применять его на практике не обязательно. Однако понимать (хотя бы концептуально, опуская множество деталей реализации), как в целом работает система, которую ты сопровождаешь - важно и нужно, даже если твоя зона ответственности достаточно мала.

Спасибо за внимание!

 

Комментарии (2)


  1. dmitrye1
    21.08.2025 17:20

    Принцип локальности данных не очень дружит с облаками, увы.


  1. SergeiMinaev
    21.08.2025 17:20

    Сам я являюсь разработчиком, и ежедневно взаимодействую с различными СУБД – в основном, с пресловутой PostgreSQL.

    "Пресловутый - широко известный своими сомнительными, отрицательными качествами". Вроде с постгрес в этом плане всё ок.