HDFS Flashcards
(19 cards)
В чем проблема мелких файлов в HDFS? Как ее решить?
Проблема — в метаданных.
HDFS оптимизирован под крупные файлы (гигабайты и выше). Каждый файл и блок — это метаинформация, которую хранит NameNode в оперативной памяти.
Если файлов слишком много (например, миллионы файлов по 1 КБ), то:
память NameNode забивается метаданными
падает производительность
система может упереться в лимит по количеству файлов, а не по диску
Что считается „маленьким файлом“:
– Любой файл значительно меньше стандартного HDFS-блока (обычно < 64 MB или < 128 MB).
– На практике проблема начинается, когда средний размер файлов в вашем наборе данных становится, скажем, десятки или сотни килобайт.
Как ее решить:
Как решить проблему? Провести “compaction” данных, т.е. объединить много маленьких файлов в малое количество б’ольших файлов. Например, прочитать партицию Spark или подобным инструментом, записать в соседнюю директорию, а потом произвести своп. Проверить, что файлы по прежнему читаются и их стало сильно меньше, и потом удалить прошлую версию партиции. Тогда куча мусорных метаданных удалится и из NameNode с Metastore. Удобнее всего проводить эту операцию при охлаждении данных, когда в архив переносятся файлы с объединением партиций (дни до недель или месяцев, месяцы до кварталов или лет).
Ну и иногда можно договориться с бэкендерами и попросить накапливать батчи на их стороне перед записью, чтобы файлы стали крупнее
Почему namenode не хочется хранить инфу о большом количестве файлов?
Потому что NameNode хранит всю метаинформацию об HDFS-файлах в оперативной памяти: на каждый файл (а точнее — на каждый его блок) создаётся объект с правами, списком блоков, репликациями и т. д. Чем больше мелких файлов, тем больше таких объектов, и оперативка у NameNode быстро кончается:
Рост потребления RAM — на каждый файл создаётся запись, и при миллионах файлов NameNode может просто не вместить всю эту информацию.
Замедление операций — каждая операция (листинг каталога, открытие файла) требует обхода и поиска по огромному объёму метаданных в памяти.
Поэтому в HDFS стремятся минимизировать число маленьких файлов и класть данные крупными блоками (ближе к размеру блока HDFS, например 128 MB).
– Если память NameNode заканчивается, JVM может выбросить OOM, и сервис упадёт.
– Даже без полного краша частые долгие паузы GC сделают NameNode недоступным.
Метаданые ведь мало весят почему namenode не может выдержать нагрузку то
Хотя сами «сырые» данные метаданных (имена файлов, права, размеры) могут занимать килобайты, на каждый файл (и каждый его блок) в NameNode создаётся целый набор Java-объектов:
Множество объектов на файл
На один HDFS-файл обычно приходится:
объект FileInode/FileStatus,
объекты для каждого блока (BlockInfo) и списка DataNode-реплик,
структуры для списка открытых/приподнятых дескрипторов и логирования lease.
Дополнительные структуры
NameNode строит индексы (hash-таблицы, деревья) для быстрого поиска по путям и блокам. Эти структуры сами по себе «тяжелее» данных.
CPU-накладные расходы
Каждая операция — ls, open, rename — теперь должна искать запись среди огромного числа объектов, что увеличивает нагрузку на CPU.
Вывод: метаданные в HDFS — это не просто несколько байт, а сложный набор взаимосвязанных Java-объектов и индексов. Чем больше «маленьких» файлов, тем больше эти структуры, тем выше потребление памяти и CPU, и тем чаще «падают» или «тупят» NameNode.
Как устроены блоки в HDFS?
Каждый файл в HDFS разбивается на блоки фиксированного размера (по умолчанию 128 МБ).
Эти блоки распределяются по DataNode’ам.
Один блок может дублироваться на нескольких узлах (репликация, по умолчанию 3).
Все блоки логически объединяются в единый файл, но физически лежат на разных машинах.
Почему NameNode может стать узким местом?
Потому что NameNode:
Хранит все метаданные: дерево каталогов, список файлов, расположение блоков и т.д.
Держит всё это в оперативной памяти (RAM).
Если слишком много файлов/блоков → память переполняется → тормоза или краш
NameNode — это единоточка управления. Он не участвует в передаче данных, но без него ничего не прочитаешь и не запишешь. Если он падает — кластер “слепнет”.
Как сделать так, чтобы NameNode не был узким местом?
Есть несколько подходов:
- High Availability (HA) NameNode
В кластере работают два NameNode: Active и Standby.
Используется общий журнал изменений (EditLog) на диске или в ZooKeeper + JournalNode.
При падении активного NameNode происходит автоматическое переключение (failover). - Federation (HDFS Federation)
Разбиваем пространство имён (namespace) на несколько независимых NameNode.
Каждый управляет своей частью файловой системы (например, /logs, /user, /data).
DataNode могут обслуживать сразу несколько NameNode.
📌 В чём разница:
HA → отказоустойчивость, 1 namespace.
Federation → масштабирование, несколько namespace.
Часто используют вместе: Federation + HA на каждом NameNode.
Что будет если NameNode упадет?
NameNode — единственный компонент, хранящий метаданные HDFS (иноды, блок-метаданные, дерево каталогов) в памяти.
При сбое Active NameNode кластер не сможет обслуживать операции: ни чтение, ни запись, ни листинг. Клиенты продолжат получать ошибки Connection Refused или подобные.
Если не настроен High Availability (HA), доступ к данным будет полностью потерян до перезапуска NameNode.
Сколько NameNode может быть?
В классической конфигурации HDFS — только один Active NameNode.
Для отказоустойчивости настраивают NameNode HA: один Active и как минимум один Standby NameNode. Standby держит реплику метаданных, готов подняться вместо Active при сбое.
Таким образом, в HA-кластере может быть 2+ NameNode (Active + Standby), но одновременно активным всегда только один.
Можно ли создать свой формат файлов в Hive?
Да. Hive поддерживает пользовательские SerDe и InputFormat/OutputFormat.
Как устроена архитектура HDFS? Что там есть? Как работает изнутри?
Hadoop — распределенная система для хранения и обработки больших данных, состоящая из трех ключевых компонентов(и одного набора утилит):
HDFS (Hadoop Distributed File System):
NameNode: Управляет метаданными (расположение блоков данных)
DataNode:Хранит данные в виде блоков (по умолчанию 128/256 МБ) и обеспечивает репликацию (обычно 3 копии)
YARN (Yet Another Resource Negotiator) — ключевой компонент Hadoop, отвечающий за управление ресурсами кластера и планирование задач
Позволяет запускать запускать различные фреймворки обработки данных, помимо встроенного MapReduce самый популярный пример взаимодействия - Spark
Архитектура YARN:
ResourceManager: Глобальное распределение ресурсов между приложениями
NodeManager:На каждом узле отслеживает ресурсы (CPU, RAM) и отчитывается ResourceManager
ApplicationMaster: Для каждого приложения запрашивает ресурсы и управляет всем жизненным циклом задачи
Таким образом за счёт YARN-а Hadoop приобретает свою главную фишку в виде удобной интеграции с другими мощными сервисами - Spark, Flink, Trino, Hive, Hbase, etc…
MapReduce — Фреймворк для параллельной обработки больших данных, разбивающий задачи на два этапа: Map и Reduce.
Map:
Обрабатывает входные данные (разделенные на блоки в HDFS)
Генерирует пары ключ - значение
Shuffle & Sort:
Группирует пары по ключу и сортирует их (происходит автоматически между Map и Reduce).
Reduce:
Агрегирует результаты (например, суммирует значения или считает среднее)
Hadoop Common - Набор встроенных утилит, в основном различные API, аутентификация и прочие utility-штуки
Как происходит запись файла 1 гб? Записать с локалки на HDFS и как он запишется
Client (клиентская программа) отправляет запрос на загрузку файла в NameNode.
NameNode решает, где разместить блоки файла (обычно размер блока — 128 МБ, поэтому 1 ГБ файла будет разбит примерно на 8 блоков).
DataNode — это нода, которая хранит сами данные. File blocks будут записаны на несколько DataNode, при этом каждый блок будет реплицирован на 3 узла (по умолчанию, можно изменить через dfs.replication).
Когда файл записан, NameNode обновляет свои метаданные, чтобы включить информацию о новом файле, его блоках и их размещении по DataNode.
Где мы делим файл на блоки?
Файл делится на блоки ещё до того, как он попадает на DataNode.
Формально деление и управление блоками происходит на клиентской стороне, при взаимодействии с NameNode.
Процесс записи:
Клиент сообщает NameNode, что хочет создать файл.
NameNode резервирует блоки и сообщает клиенту, на какие DataNode писать (с учётом репликации).
Клиент сам разбивает файл на блоки и пишет каждый блок напрямую на указанные DataNode.
То есть разбиение не на DataNode и не на NameNode, а на клиенте.
DataNode просто принимает уже готовые куски данных.
Что если остается кусок данных? Где он хранится?
Весь блок забиваться не будет. Будет маленький кусочек
Каждый блок — независимая единица хранения.
То, что он не заполнен до конца — нормально, HDFS не выравнивает блокы.
Если останется хвост, то он просто будет храниться как отдельный блок с меньшим объёмом.
Какие сервисы у YARN? Как он работает?
ResourceManager (RM)
Центральный компонент, отвечает за распределение ресурсов по кластерам. Состоит из:
Scheduler — принимает заявки и распределяет ресурсы (не запускает задачи).
ApplicationManager — управляет жизненным циклом приложений (следит за их запуском, завершением и перезапусками).
NodeManager (NM) - Запускается на каждом узле кластера. менеджер узла – агент, запущенный на узле кластера, который отвечает за отслеживание используемых вычислительных ресурсов (CPU, RAM и пр.), управление логами и отправку отчетов об использовании ресурсов планировщику. NodeManager управляет абстрактными контейнерами – ресурсами узла, доступными для конкретного приложения.
ApplicationMaster (AM)
Мастер приложения, ответственный за планирование его жизненного цикла, координацию и отслеживание статуса выполнения, включая динамическое масштабирование потребления ресурсов, управление потоком выполнения, обработку ошибок и искажений вычислений, выполнение локальных оптимизаций. Каждое приложение имеет свой экземпляр ApplicationMaster. ApplicationMaster выполняет произвольный пользовательский код и может быть написан на любом языке программирования благодаря расширяемым протоколам связи с менеджером ресурсов и менеджером узлов.
Контейнер (Container) – набор физических ресурсов (ЦП, память, диск, сеть) в одном вычислительном узле кластера.
Как работает:
1. клиентское приложение отправляет запрос в кластер;
2. менеджер ресурсов выделяет необходимые ресурсы для контейнера и запускает ApplicationMaster для обслуживания этого приложения;
3. ApplicationMaster отправляет запрос менеджеру узла NodeManager, включая контекст запуска контейнера Container Launch Context (CLC);
4. ApplicationMaster выделяет контейнеры для приложения в каждом узле и контролирует их работу до завершения работы приложения;
5. Для запуска контейнера менеджер узла копирует в локальное хранилище все необходимые зависимости (данные, исполняемые файлы, архивы);
6. по завершении задачи мастер приложения отменяет выделенный контейнер в диспетчере ресурсов, завершая жизненный цикл распределенного задания.
7. клиент может отслеживать состояние распределенного приложения, обращаясь к менеджеру ресурсов или сразу к мастеру приложения.
Мы делаем Spark submit и когда мы узнаем получится ли выдать ресурсы?
Ты вызываешь spark-submit.
Spark-клиент отправляет запрос в YARN ResourceManager: “дай мне ресурсы для запуска приложения”.
YARN решает — можно ли выдать ресурсы (CPU, RAM, слоты):
Если да → запускается ApplicationMaster (Spark driver)
Если нет → приложение ждёт в очереди
ApplicationMaster (драйвер) запрашивает Executor’ы у NodeManager’ов.
Когда Executors поднялись — Spark начинает выполнять задачу (job → stage → task).
Как понять, получишь ли ты ресурсы при spark-submit?
Сначала решает YARN ResourceManager:
хватает ли памяти, ядер
есть ли лимиты/квоты
нет ли более приоритетных заданий
Если не хватает → приложение в очереди (можно смотреть через YARN UI / CLI)
Если хватает → поднимается Spark Driver и Executors
Статусы у джоб? Что они значат? в YARN
Статусы джоб в YARN (ACCEPTED, RUNNING, FINISHED, FAILED и др.) позволяют отслеживать этапы выполнения Spark-приложений.
При отладке пайплайнов я часто использовала ResourceManager UI или yarn application -status, чтобы проверить, зависла ли джоба, упала или успешно
ACCEPTED Джоба принята ResourceManager, но ещё не запущена.
RUNNING Джоба выполняется. Выделены ресурсы, идут таски.
FINISHED Джоба успешно завершилась.
FAILED Ошибка выполнения. Причина в логах ApplicationMaster.
Как масштабировать Hadoop-кластер?
- Добавление новых DataNode (горизонтальное масштабирование)
Самый распространённый способ.
Просто добавляешь сервер с установленным Hadoop и настраиваешь его как DataNode.
Плюсы: увеличивается дисковая ёмкость и параллелизм обработки.
Минусы: при очень большом кластере может потребоваться перенастройка NameNode (он остаётся единственным). - Увеличение ресурсов на существующих узлах (вертикальное масштабирование)
Расширяешь CPU, RAM, диск на существующих машинах.
Позволяет запускать больше контейнеров YARN на каждом узле и ускорить задачи.
Ограничение: физические пределы одной машины. - Масштабирование компонентов YARN
Добавляешь NodeManager’ы на новых машинах.
ResourceManager сам начинает использовать их в расписании. - Балансировка данных
После добавления новых DataNode запускается hdfs balancer, чтобы равномерно перераспределить блоки HDFS по кластерам.
hdfs balancer -threshold 10 - NameNode High Availability (HA)
Если кластер большой, один NameNode может стать узким местом.
Включается HA-режим: Active/Standby NameNode с Zookeeper Failover Controller (ZKFC). - Использование Federation (при очень крупных кластерах)
Делит HDFS на несколько независимых NameNode + Namespace.
Позволяет масштабировать хранилище и избегать перегрузки одного NameNode.
Как выглядят партиции в HDFS? Если мы попытаемся в Hive партиционировать, то как это отразится в HDFS?
Мы пытаемся создать таблицу в Хадупе, мы аналитик, например спарк не знаем полезли в Hive, пишем DDL таблицы и говорим, что мы будем партиционировать таблицу по какой-то дате и таблица создалась. Как мы потом сможем считать эти данные? По каким путям? Уже в спарке? Уже потом?
В Hadoop/Hive-партиционировании каждая партиция физически представлена директорией, имя которой кодирует значение партиционирующего столбца. Формат называется column=value
Hive просто создаёт на HDFS подпапки column=value, в которых лежат файлы данных. Spark и другие движки читают эти папки как единый датасет и автоматически отсекают (prune) только нужные директории по фильтру.
Он создаст много таблиц, но физически они в HDFS будут как в отдельных директориях лежать. У нас поддиректории будут создаваться и внутри этих поддиректорий уже будут parquetы лежать
В HDFS каждая партиция Hive — это просто папка вида column=value внутри каталога таблицы.
Добавление/удаление партиции в Hive — это создание/удаление соответствующей папки в HDFS.
Как будет обрабатываться запрос между NameNode и DataNode?
Client (клиентская программа) отправляет запрос на загрузку файла в NameNode.
NameNode отвечает, на какие DataNode’ы отправлять каждый блок файла (по умолчанию размер блока — 128 МБ, значит файл 1 ГБ будет разбит клиентом примерно на 8 блоков).
Client сам разбивает файл на блоки и передаёт их на указанные DataNode. Каждый блок записывается на несколько узлов (по умолчанию 3 реплики, настраивается через dfs.replication) с использованием pipeline-репликации.
Когда все блоки записаны, NameNode обновляет метаданные, включая путь к файлу, список блоков и размещение этих блоков по DataNode.
В GP мы все данные получали от мастера. Мастер отправлял запросы на сегменты, сегменты их обрабатывали и возвращали на мастер, финальная фильтрация происходит и возвращается клиенту. Будет ли отличаться архитектура от GP?
В Greenplum:
Client (или приложение) отправляет запрос на master node.
Master node:
анализирует, планирует и оптимизирует запрос,
рассылает подзапросы по segment-нодам.
Segment-ноды обрабатывают данные (чтение, join, group by и т.д.).
Результаты операций возвращаются на master, где происходит:
возможно — финальная агрегация или фильтрация,
затем результат отдаётся клиенту.
Master координирует, агрегирует и управляет выполнением плана.
В HDFS:
Client работает напрямую с NameNode только на этапе метаданных:
узнаёт, где лежат блоки нужного файла.
Дальше Client подключается напрямую к DataNode’ам и считывает или записывает данные.
NameNode не участвует в передаче данных — только управляет метаинформацией (где и какие блоки лежат).
NameNode не агрегирует данные, не передаёт их и не координирует выполнение запроса как master в GP.